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.
Posted by Vartika Agarwal, Technical Program Manager, Identity & Authentication, and Wesley Chun, Developer Advocate, Google
As we indicated several years ago, we are moving away from the OAuth 1.0 protocol in order to focus our support on the current OAuth standard, OAuth 2.0, which increases security and reduces complexity for developers. OAuth 1.0 (3LO)1 was shut down on April 20, 2015. During this final phase, we will be shutting down OAuth 1.0 (2LO) on October 20, 2016. The easiest way to migrate to the new standard is to use OAuth 2.0 service accounts with domain-wide delegation.
If the migration for applications using these deprecated protocols is not completed before the deadline, those applications will experience an outage in their ability to connect with Google, possibly including the ability to sign-in, until the migration to a supported protocol occurs. To avoid any interruptions in service for your end-users, it is critical that you work to migrate your application(s) prior to the shutdown date.
With this step, we continue to move away from legacy authentication/authorization protocols, focusing our support on modern open standards that enhance the security of Google accounts and that are generally easier for developers to integrate with. If you have any technical questions about migrating your application, please post them to Stack Overflow under the tag google-oauth.
1 3LO stands for 3-legged OAuth: there's an end-user that provides consent. In contrast, 2-legged (2LO) doesn’t involve an end-user and corresponds to enterprise authorization scenarios such as enforcing organization-wide policy control access.
Update (2015-06-15): The sunset date listed below has been changed from June 26th to July 6th, 2015.
Posted by Eric Koleda, Developer Platform Engineer
OAuth is the de facto standard for authorization today and is used by most modern APIs. Apps Script handles the OAuth flow automatically for dozens of built-in and advanced services, but until recently only had limited support for connecting to other OAuth-protected APIs such as Twitter, etc. The URL Fetch service’s OAuthConfig class only works with the older OAuth 1.0 standard and only allows the developer of the script (not its users) to grant access to their data. To address this, we’ve create two new open source libraries:
OAuthConfig
With only a few clicks, you can add these libraries to your scripts. The full source code is available on GitHub if you need to tinker with how they work. These libraries allow for greater control over the OAuth flow, including the ability for users to grant access separately, a long standing feature request from the community.
We believe that these open libraries are a better alternative to our previous solution, and therefore we are deprecating the OAuthConfig class. The class will continue to function until July 6, 2015, after which it will be removed completely and any scripts that use it will stop working We’ve prepared a migration guide that walks you through the process of upgrading your existing scripts to use these new libraries.
Separate from these changes in Apps Script and as announced in 2012, all Google APIs will stop supporting OAuth 1.0 for inbound requests on April 20, 2015. If you use OAuthConfig to connect to Google APIs, you will need to migrate before that date. Update your code to use the OAuth2 library or the API’s equivalent Advanced Service if one exists.
We see Apps Script and Sheets as the perfect hub for connecting together data inside and outside of Google, and hope this additional OAuth functionality makes it an even more compelling platform.
Editor's note: This post by Google Senior Product Manager Justin Smith has been cross-posted from the Google Code blog because we think it'll be of great interest to Google Apps developers. -- Ryan Boyd
In the coming weeks we will be making three changes to the experimental OAuth 2.0 endpoint. We expect the impact to be minimal, and we’re emailing developers who are most likely to be affected.
https://www.example.com/back?error=access_denied.
https://www.example.com/back?error=access_denied
https://www.example.com/back#error=access_denied
approval_prompt=force
access_type=offline
https://accounts.google.com/o/oauth2/auth? client_id=21302922996.apps.googleusercontent.com& redirect_uri=https://www.example.com/back& scope=https://www.google.com/m8/feeds/& response_type=code
https://accounts.google.com/o/oauth2/auth? client_id=21302922996.apps.googleusercontent.com& redirect_uri=https://www.example.com/back& scope=https://www.google.com/m8/feeds/& response_type=code& access_type=offline& approval_prompt=force
Since March of this year, Google has supported OAuth 2.0 for many APIs, including Google Data APIs such as Google Calendar, Google Contacts and Google Documents List. Google's implementation of OAuth 2.0 introduces many advantages compared to OAuth 1.0 such as simplicity for developers and a more polished user experience.
We’ve just added support for this authorization mechanism to the gdata-python-client library-- let’s take a look at how it works by retrieving an access token for the Google Calendar and Google Documents List APIs and listing protected data.
First, you will need to retrieve or sync the project from the repository using Mercurial:
hg clone https://code.google.com/p/gdata-python-client/
For more information about installing this library, please refer to the Getting Started With the Google Data Python Library article.
Now that the client library is installed, you can go to your APIs Console to either create a new project, or use information about an existing one from the API Access pane:
Your application will require the user to grant permission for it to access protected APIs on their behalf. It must redirect the user over to Google's authorization server and specify the scopes of the APIs it is requesting permission to access.
Available Google Data API’s scopes are listed in the Google Data FAQ.
Here's how your application can generate the appropriate URL and redirect the user:
import gdata.gauth # The client id and secret can be found on your API Console. CLIENT_ID = '' CLIENT_SECRET = '' # Authorization can be requested for multiple APIs at once by specifying multiple scopes separated by # spaces. SCOPES = ['https://docs.google.com/feeds/', 'https://www.google.com/calendar/feeds/'] USER_AGENT = '' # Save the token for later use. token = gdata.gauth.OAuth2Token( client_id=CLIENT_ID, client_secret=CLIENT_SECRET, scope=' '.join(SCOPES), user_agent=USER_AGENT) # The “redirect_url” parameter needs to match the one you entered in the API Console and points # to your callback handler. self.redirect( token.generate_authorize_url(redirect_url='http://www.example.com/oauth2callback'))
If all the parameters match what has been provided by the API Console, the user will be shown this dialog:
When an action is taken (e.g allowing or declining the access), Google’s authorization server will redirect the user to the specified redirect URL and include an authorization code as a query parameter. Your application then needs to make a call to Google’s token endpoint to exchange this authorization code for an access token.
import atom.http_core url = atom.http_core.Uri.parse_uri(self.request.uri) if 'error' in url.query: # The user declined the authorization request. # Application should handle this error appropriately. pass else: # This is the token instantiated in the first section. token.get_access_token(url.query)
The redirect handler retrieves the authorization code that has been returned by Google’s authorization server and exchanges it for a short-lived access token and a long-lived refresh token that can be used to retrieve a new access token. Both access and refresh tokens are to be kept private to the application server and should never be revealed to other client applications or stored as a cookie.
To store the token object in a secured datastore or keystore, the gdata.gauth.token_to_blob() function can be used to serialize the token into a string. The gdata.gauth.token_from_blob() function does the opposite operation and instantiate a new token object from a string.
gdata.gauth.token_to_blob()
gdata.gauth.token_from_blob()
Now that an access token has been retrieved, it can be used to authorize calls to the protected APIs specified in the scope parameter.
import gdata.calendar.client import gdata.docs.client # Access the Google Calendar API. calendar_client = gdata.calendar.client.CalendarClient(source=USER_AGENT) # This is the token instantiated in the first section. calendar_client = token.authorize(calendar_client) calendars_feed = client.GetCalendarsFeed() for entry in calendars_feed.entry: print entry.title.text # Access the Google Documents List API. docs_client = gdata.docs.client.DocsClient(source=USER_AGENT) # This is the token instantiated in the first section. docs_client = token.authorize(docs_client) docs_feed = client.GetDocumentListFeed() for entry in docs_feed.entry: print entry.title.text
For more information about OAuth 2.0, please have a look at the developer’s guide and let us know if you have any questions by posting them in the support forums for the APIs you’re accessing.
Updated 9/30/2011 to fix a small typo in the code
Gmail servers support the standard IMAP and POP protocols for email retrieval but sometimes you only need to know whether there are any new messages in your inbox. Using any of the two protocols mentioned above may seem like an overkill in this scenario and that’s why Gmail also exposes a read only feed called Gmail Inbox Feed which you can subscribe to and get the list of unread messages in your inbox.
The Gmail Inbox Feed is easily accessible by pointing your browser to https://mail.google.com/mail/feed/atom and authenticating with your username and password if you are not already logged in.
Using basic authentication to access the inbox feed doesn’t provide a very good user experience if we want delegated access. In that case, we should instead rely on the OAuth authorization standard, which is fully supported by the Gmail Inbox Feed.
OAuth supports two different flows. With 3-legged OAuth, an user can give access to a resource he owns to a third-party application without sharing his credentials. The 2-legged flow, instead, resembles a client-server scenario where the application is granted domain-wide access to a resource.
Let’s write a .NET application that uses 2-legged OAuth to access the Gmail Inbox Feed for a user in the domain and list unread emails. This authorization mechanism also suits Google Apps Marketplace developers who want to add inbox notifications to their applications.
There is no dedicated client library for this task and the Inbox Feed is not based on the Google Data API protocol but we’ll still use the .NET library for Google Data APIs for its OAuth implementation.
First, create a new C# project and add a reference to the Google.GData.Client.dll released with the client library. Then add the following using directives to your code:
using System; using System.Linq; using System.Net; using System.Net.Mail; using System.Xml; using System.Xml.Linq; using Google.GData.Client;
The next step is to use 2-legged OAuth to send an authorized GET request to the feed URL. In order to do this, we need our domain’s consumer key and secret and the username of the user account we want to access the inbox feed for.
string CONSUMER_KEY = "mydomain.com"; string CONSUMER_SECRET = "my_consumer_secret"; string TARGET_USER = "test_user"; OAuth2LeggedAuthenticator auth = new OAuth2LeggedAuthenticator("GmailFeedReader", CONSUMER_KEY, CONSUMER_SECRET, TARGET_USER, CONSUMER_KEY); HttpWebRequest request = auth.CreateHttpWebRequest("GET", new Uri("https://mail.google.com/mail/feed/atom/")); HttpWebResponse response = request.GetResponse() as HttpWebResponse;
The response is going to be a standard Atom 0.3 feed, i.e. an xml document that we can load into an XDocument using the standard XmlReader class:
XDocument
XmlReader
XmlReader reader = XmlReader.Create(response.GetResponseStream()); XDocument xdoc = XDocument.Load(reader); XNamespace xmlns = "http://purl.org/atom/ns#";
All the parsing can be done with a single LINQ to XML instruction, which iterates the entries and instantiates a new MailMessage object for each email, setting its Subject, Body and From properties with the corresponding values in the feed:
MailMessage
Subject
Body
From
var messages = from entry in xdoc.Descendants(xmlns + "entry") from author in entry.Descendants(xmlns + "author") select new MailMessage() { Subject = entry.Element(xmlns + "title").Value, Body = entry.Element(xmlns + "summary").Value, From = new MailAddress( author.Element(xmlns + "email").Value, author.Element(xmlns + "name").Value) };
At this point, messages will contain a collection of MailMessage instances that we can process or simply dump to the console as in the following snippet:
Console.WriteLine("Number of messages: " + messages.Count()); foreach (MailMessage entry in messages) { Console.WriteLine(); Console.WriteLine("Subject: " + entry.Subject); Console.WriteLine("Summary: " + entry.Body); Console.WriteLine("Author: " + entry.From); }
If you have any questions about how to use the Google Data APIs .NET Client Library to access the Gmail Inbox Feed, please post them in the client library discussion group.
We have released version 1.9 of the .NET Library for Google Data APIs and it is available for download.
This version adds the following new features:
This new version also removes the client library for the deprecated Google Base API and fixes 20 bugs.
For more details, please check the Release Notes and remember to file feature requests or bugs in the project issue tracker.
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
https://www.googleapis.com/tasks/v1/users/username/lists?key=<API_KEY>
Tasks
// Initializing the Tasks API serviceTasks service = new Tasks("2-LO Tasks Test", httpTransport, jsonFactory);service.accessKey = API_KEY;
Posted by Ankur Jain, Software Engineer
Want to weigh in on this topic? Discuss on Buzz
By Jeff Morgan, Senior Technical Consultant at Appirio
01 LOGIN username@googlemail.com P4$$w0rd
01 AUTHENTICATE XOAUTH R0VUIGh0dHBzOi8vbWFpbC5nb29nbGUuY29tL21haWwvYi91c2VybmFtZUBnb29nbGVtYWlsLmNvbS9pbWFwLyBvYXV0aF9jb25zdW1lcl9rZXk9ImFub255bW91cyIsb2F1dGhfbm9uY2U9IjEzMDQwNzM0Nzc4MTA1MDA2NTQzIixvYXV0aF9zaWduYXR1cmU9ImNhdWdodHlvdXBlZWtpbmciLG9hdXRoX3NpZ25hdHVyZV9tZXRob2Q9IkhNQUMtU0hBMSIsb2F1dGhfdGltZXN0YW1wPSIxMjY5Mzc3NzU5IixvYXV0aF90b2tlbj0ibm90cmVhbGx5IixvYXV0aF92ZXJzaW9uPSIxLjAiCg==