The
google.accounts.oauth2
JavaScript library helps you prompt for user
consent and obtain an access token to work with user data. It is based upon the
OAuth 2.0
implicit grant
flow and designed to allow you to either call Google
APIs directly using REST and CORS, or to use our
Google APIs client library for
JavaScript
(also known as
gapi.client
) for simple, flexible access to our
more complex APIs.
Before accessing protected user data from a browser, users on your site trigger
Google's web based account chooser, sign-in, and consent processes, and lastly
Google's OAuth servers issue and return an access token to your web app.
In the token based authorization model, there is no need to store per-user
refresh tokens on your backend server.
It is recommended that you follow the approach outlined here instead of the
techniques covered by the older
OAuth 2.0 for Client-side Web Applications
guide.
Setup
Find or create a client ID by following the steps described in the
Get your
Google API client ID
guide. Next,
add the client library
to the pages
on your site that will be calling Google APIs. Lastly, initialize the token
client. Typically, this is done within the client library's
onload
handler.
Initialize a token client
Call
initTokenClient()
to initialize a new token client with your web app's
client ID, optionally you may include a list of one or more
scopes
the user
needs to access:
const client = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
callback: (response) => {
...
},
});
Trigger the OAuth 2.0 token flow
Use the
requestAccessToken()
method to trigger the token UX flow and obtain an
access token. Google prompts the user to:
- Choose their account,
- sign-in to the Google Account if not already signed-in,
- grant consent for your web app to access each requested scope.
A user gesture triggers the token flow:
<button onclick="client.requestAccessToken();">Authorize me</button>
Google then returns a
TokenResponse
containing an access token and list of
scopes the user has granted access to, or an error, to your callback handler.
Users may close the account chooser or sign-in windows, in which case your
callback function will not be invoked.
How to handle consent
The design and user experience for your app should be implemented only after a
thorough review of Google's
OAuth 2.0 Policies
. These policies cover
working with multiple scopes, when and how to handle user consent, and more.
Incremental authorization
is a policy and app design methodology used to
request access to resources, using scopes, only as needed rather than up-front
and all at once. Users may approve or reject sharing of the individual resources
requested by your app, this is known as
granular permissions
.
During this process Google prompts for user consent, individually listing each
requested scope, users select the resources to be shared with your app, and
lastly, Google invokes your callback function to return an Access token and user
approved scopes. Your app then safely handles the various different outcomes
possible with granular permissions.
Incremental authorization
For web apps, the following two high-level scenarios demonstrate incremental
authorization using:
- A single-page Ajax app, often using
XMLHttpRequest
with dynamic access to
resources.
- Multiple web-pages, resources are separated and managed on a per page basis.
These two scenarios are presented to illustrate design considerations and
methodologies, but are not intended to be comprehensive recommendations on how
to build consent into your app. Real-world apps may use a variation or
combination of these techniques.
Ajax
Add support for incremental authorization to your app by making multiple calls
to
requestAccessToken()
and using the
OverridableTokenClientConfig
object's
scope
parameter to request individual scopes at the time they are needed and
only when necessary. In this example resources will be requested and visible
only after a user gesture expands a collapsed content section.
Ajax app
|
Initialize the token client on page load:
const client = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
callback: "onTokenResponse",
});
Request consent and obtain access tokens through user gestures,
click `+` to open:
Docs to read
Show recent documents
client.requestAccessToken(
overrideConfig = ({
scope = 'https://www.googleapis.com/auth/documents.readonly'
})
);
Upcoming events
Show calendar info
client.requestAccessToken(
overrideConfig = ({
scope = 'https://www.googleapis.com/auth/calendar.readonly'
})
);
Photo carousel
Display photos
client.requestAccessToken(
overrideConfig = ({
scope = 'https://www.googleapis.com/auth/photoslibrary.readonly'
})
);
|
Each call to
requestAccessToken
triggers a user consent moment, your app will
have access only to those resources required by the section a user chooses to
expand, thus limiting resource sharing through user choice.
Multiple web-pages
When designing for incremental authorization, multiple pages are used to request
only the scope(s) required to load a page, reducing complexity and the need to
make multiple calls to obtain user consent and retrieve an access token.
Multi-page app
|
Web page
|
Code
|
Page 1. Docs to read
|
const client = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
callback: "onTokenResponse",
scope: 'https://www.googleapis.com/auth/documents.readonly',
});
client.requestAccessToken();
|
Page 2. Upcoming events
|
const client = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
callback: "onTokenResponse",
scope: 'https://www.googleapis.com/auth/calendar.readonly',
});
client.requestAccessToken();
|
Page 3. Photo carousel
|
const client = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
callback: "onTokenResponse",
scope: 'https://www.googleapis.com/auth/photoslibrary.readonly',
});
client.requestAccessToken();
|
|
Each page requests the necessary scope and obtains an access token by calling
initTokenClient()
and
requestAccessToken()
at load time. In this scenario,
individual web pages are used to clearly separate user functionality and
resources by scope. In a real-world situation, individual pages may request
multiple related scopes.
Granular permissions
Granular permissions are handled the same way in all scenarios; after
requestAccessToken()
invokes your callback function and an access token
returned, check that the user has approved the requested scopes using
hasGrantedAllScopes()
or
hasGrantedAnyScope()
. For example:
const client = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly \
https://www.googleapis.com/auth/documents.readonly \
https://www.googleapis.com/auth/photoslibrary.readonly',
callback: (tokenResponse) => {
if (tokenResponse && tokenResponse.access_token) {
if (google.accounts.oauth2.hasGrantedAnyScope(tokenResponse,
'https://www.googleapis.com/auth/photoslibrary.readonly')) {
// Look at pictures
...
}
if (google.accounts.oauth2.hasGrantedAllScopes(tokenResponse,
'https://www.googleapis.com/auth/calendar.readonly',
'https://www.googleapis.com/auth/documents.readonly')) {
// Meeting planning and review documents
...
}
}
},
});
Any previously accepted grants from prior sessions or requests will also be
included in the response. A record of user consent is maintained per user and
Client ID, and persists across multiple calls to
initTokenClient()
or
requestAccessToken()
. By default, user consent is only necessary the first
time a user visits your website and requests a new scope but may be requested on
every page load using
prompt=consent
in Token Client config objects.
Working with tokens
In the Token model, an access token is not stored by the OS or browser, instead
a new token is first obtained at page load time, or subsequently by triggering a
call to
requestAccessToken()
through a user gesture such as a button press.
Using REST and CORS with Google APIs
An access token can be used to make authenticated requests to Google APIs using
REST and CORS. This enables users to sign-in, grant consent, Google to issue an
access token and your site to work with the user's data.
In this example, view the signed-in users upcoming calendar events using the
access token returned by
tokenRequest()
:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.googleapis.com/calendar/v3/calendars/primary/events');
xhr.setRequestHeader('Authorization', 'Bearer ' + tokenResponse.access_token);
xhr.send();
See
How to use CORS to access Google APIs
for more.
The next section covers how to easily integrate with more complex APIs.
Working with the Google APIs JavaScript library
The token client works with the
Google API Client Library for JavaScript
See the code snippet below.
const client = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_GOOGLE_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
callback: (tokenResponse) => {
if (tokenResponse && tokenResponse.access_token) {
gapi.client.setApiKey('YOUR_API_KEY');
gapi.client.load('calendar', 'v3', listUpcomingEvents);
}
},
});
function listUpcomingEvents() {
gapi.client.calendar.events.list(...);
}
Token expiration
By design, access tokens have a short lifetime. If the access token expires
prior to the end of the user's session, obtain a new token by calling
requestAccessToken()
from a user-driven event such as a button press.
Using an access token to revoke consent
Call the
google.accounts.oauth2.revoke
method to remove user consent and
access to resources for all of the scopes granted to your app. A valid access
token is required to revoke this permission:
google.accounts.oauth2.revoke('414a76cb127a7ece7ee4bf287602ca2b56f8fcbf7fcecc2cd4e0509268120bd7', done => {
console.log(done);
console.log(done.successful);
console.log(done.error);
console.log(done.error_description);
});