When developers build software, it routinely includes modules that run on a web server, other modules that run in the browser, and others that run as native mobile apps. Both developers and the people who use their software typically think of all these modules as part of a single app.
Google’s OAuth 2.0 implementation supports this view of the world. To use any of the OAuth2.0-based services, you must set up your software in the Google Developers Console. The unit of organization in the Developers Console is a "project," which can correspond to a multi-component app. For each project, you can provide branding information, and you must specify which APIs the app will access. Each component of a multi-component app is identified by a client ID, a unique string that is generated in the Developers Console.
Cross-client authorization goals
When an app uses OAuth 2.0 for authorization, the app acts on a user's behalf to request an OAuth 2.0 access token for access to a resource, which the app identifies by one or more scope strings. Normally, the user is asked to approve the access.
When a user grants access to your app for a particular scope, the user is looking at the user consent screen, which includes project-level product branding that you set up in the Google Developers Console. (For information about setting up the consent screen, see Setting up OAuth 2.0 in the Developers Console help.) Therefore, Google considers that when a user has granted access to a particular scope to any client ID in a project, the grant indicates the user's trust in the whole application for that scope.
Cross-client access tokens
Software can obtain OAuth 2.0 Access tokens in a variety of ways, depending on the platform where the code is running. For details, see Using OAuth 2.0 to Access Google APIs and Google Play Services Authorization. Normally, user approval is required when granting an access token.
Fortunately, the Google authorization infrastructure can use information about user approvals for a client ID within a given project when evaluating whether to authorize others in the same project.
The effect is that if an Android app requests an access token for a particular scope, and the requesting user has already granted approval to a web component in the same project for that same scope, the user will not be asked once again to approve. This works both ways: If access to a scope has been granted on Android, it will not be demanded again from a web component.
Android ID tokens
One benefit of cross-client identity is that you can leverage it and ID tokens to enable Android apps to talk to their home server without requiring the user to log in.
Suppose that there is a project set up in the Developers Console that contains a server-side component (an app) whose client ID is
9414861317621.apps.googleusercontent.com. Suppose that there is also a native Android app in the same project.
The Android app can call the
GoogleAuthUtil.getToken() method on behalf of any of the Google accounts on the device, and with a
scope argument value of
audience:server:client_id:9414861317621.apps.googleusercontent.com. The prefix
audience:server:client_id: is fixed, and the rest of the scope string is the client ID of the web component.
In this scenario,
GoogleAuthUtil will observe that the Android app and the web client ID are in the same project, and without user approval, return an ID token to the app, signed by Google. The ID token will contain several data fields, of which the following are particularly relevant:
aud: the client ID of the web component of the project
azp: the client ID of the Android app component of project
This ID token is designed to be transmitted to the web module over HTTPS. Before using it, the web component must do the following:
Validate the cryptographic signature. Because the token takes the form of a JSON web token or JWT and there are libraries to validate JWTs available in most popular programming languages, this is straightforward and efficient.
Ensure that the value of the
audfield is identical to its own client ID.
Once this is accomplished, the web component can have a high degree of certainty that:
The token was issued by Google.
The token was sent to a device that was being operated by the person identified in the payload's
The web component can also have good confidence that the token was obtained by the Android app identified by the client ID in the payload’s
azp field. However, non-compatible or rooted Android devices might be able to fake the
The effect is that the web component can treat the data coming from the Android client, along with the ID token, as coming from a signed-in user of the project, and act confidently on that user’s behalf to access and update that user’s data, without redundantly prompting for authentication.
Android app obtains offline access for web back-end
Suppose once again that a project includes a web component whose client ID is
9414861317621.apps.googleusercontent.com, and in this case wants to access two different resources on behalf of a user, and to do so even when the user is not online. However, the software is normally accessed interactively through an Android app.
Let us further suppose that the resources are identified by the Google
Drive scope string
https://www.googleapis.com/auth/drive.file and the
Google+ scope login string
https://www.googleapis.com/auth/plus.login, which we'll refer
In this situation, an Android app that is in the same Developers Console project as the web component can call the
GoogleAuthUtil.getToken() method, specifying any of the Google accounts on the device, and with a
scope argument value of
In this case,
GoogleAuthUtil.getToken() will first require that the user has authorized this project for access to the two scopes. Assuming this is OK, the method will return not an OAuth token, but a short-lived authorization code, which can be exchanged for an access token and a refresh token.
The Android app can then send the authorization code to its web component using HTTPS. The web component can exchange the code, as described in Handling the response, for an access token and a refresh token. The following rules apply to the web component:
When the web component exchanges the code for tokens, it should not include the
redirect_uriargument in the
When the web component receives the tokens, it must examine the ID token. Signature-checking is not required, since the token was received from Google over a secure channel. However, the web component must:
Verify that the
audfield in the ID token from Google is identical to its client ID from the Google Developers Console.
Verify that the
subfield in the ID token from Google is identical to the
subfield in the ID token that it received from the client.
Since the refresh token does not expire, it is the responsibility of the web component to store the refresh token in a secure and long-lived manner.
It is important that the Android app never attempt to exchange the code for a refresh token on its own. This would require that the app store the server's client ID and client secret. Including these in your Android app is insecure, because apps can be easily decomposed.
You can use the access token to access the required scopes for a short period of time, on the order of an hour. The refresh token does not expire unless explicitly revoked, and you can use it to obtain new access tokens at any time.
To use resources efficiently and avoid redundant work, we recommend the following application flow when an Android app commences a session of work with its web back-end:
POSTrequests for all requests to the server, and include an ID token in the body of each
POSTrequest. The ID token, which you acquire as described in the preceding section, informs the back-end of the user's identity.
In the first request of a session, also include a query to the back-end as to whether it has the appropriate level of online access to the needed scopes.
The web back-end must maintain knowledge of whether it has a working refresh token. If it doesn’t have a refresh token or discovers that its refresh token it has been revoked (which you can easily test by using the refresh token to fetch a new access token), the web back-end communicates its lack of a refresh token to the client.
If the client receives a message from its back-end indicating that the back-end does not have a working refresh token, the Android app obtains an authorization code, as described above, and transmits the code to its back-end.
The client and server proceed with their session.
In this sequence, control over the request for offline access is maintained by the server-side code, which is best positioned to know when offline access is needed. Also, if the app always requested a refresh token without checking whether the server already had one, this would eventually exhaust the finite number of refresh tokens that can be outstanding for a user/app combination.
The effect of all this is that the mobile-app component of a project can be used to obtain permanent offline access to resources for other components, with only the minimum number of approvals from the user.