## Overview

With granular permissions, consumers get more fine-grained control over what
account data they choose to share with each app. They benefit both users and
developers by providing greater control, transparency, and security. This guide
will help you to understand the necessary changes and steps to successfully
update your applications to handle granular permissions.

## What is granular permission?

Imagine you develop a productivity app that requests both email and calendar
scopes. Your users may want to use your application only for Google Calendar,
but not Gmail. With granular OAuth permissions, users can choose to only grant
Google Calendar permission but not Gmail. By letting users grant access to
specific data, this minimizes data exposure, bolsters trust, and empowers users
with privacy-first control over their digital life. It is important to design
your application to handle such scenarios.

### When more than one non-Sign-In scope is requested

#### Sign-In and non-Sign-In scopes

For applications that request both Sign-In and non-Sign-In scopes, users first
see the consent page for [Sign-In scopes](https://developers.google.com/identity/protocols/oauth2/scopes.md.txt#oauth2) (`email`, `profile`,
and `openid`). After users consent to share their basic identity information
(name, email address, and profile photo), users will see a granular permission
consent screen for the non-Sign-In scopes. In this case, the application must
check what scopes are granted by the users and can't assume users grant all
requested scopes. In the following example, the web application requests all
three Sign-In scopes and a Google drive non-Sign-In scope. After users consent
to the Sign-In scopes, users will see the granular permissions consent screen
for the Google Drive permission:

![Sign-In and non-Sign-In scopes](https://developers.google.com/static/identity/protocols/oauth2/images/examples/identity-and-nonidentity.png)

#### More than one non-Sign-In scope

A granular permission consent screen would be displayed to users when
applications request more than one non-Sign-In scope. Users can select which
permissions they want to approve to share with the application. The following is
an example granular permission consent screen requesting access to user's Gmail
messages and Google Calendar data:

![More than one non-Sign-In scope](https://developers.google.com/static/identity/protocols/oauth2/images/examples/two-nonidentity.png)

For applications that request only [Sign-In scopes](https://developers.google.com/identity/protocols/oauth2/scopes.md.txt#oauth2) (`email`,
`profile`, and `openid`), the granular permissions consent screen is not
applicable. Users either approve or deny the entire sign-in request. In the
other words, if applications only request Sign-In scopes (one, two, or all
three), the granular permission consent screen is not applicable.

For applications that request only **one** non-Sign-In scope, the granular
permission consent screen is **not** applicable. In the other words, users
either approve or deny the entire request, and there is no checkbox in the
consent screen. The following table summarizes when granular permissions consent
screen is displayed.

| Number of Sign-In scopes | Number of Non-Sign-In scopes | Granular permissions consent screen |
|---|---|---|
| 1-3 | 0 | Not applicable |
| 1-3 | 1+ | Applicable |
| 0 | 1 | Not applicable |
| 0 | 2+ | Applicable |

## Determine if your applications are affected

Conduct a thorough review of all sections within your application where Google
OAuth 2.0 authorization endpoints are utilized for permission requests. Pay
attention to those that request multiple scopes as they activate granular
permission consent screens presented to users. In such instances, make sure your
code can handle the case where users only authorize some of the scopes.

### How to determine if your application is using multiple scopes

[Inspect your app code](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions.md.txt#inspect-your-application-code) or the [outgoing network
call](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions#inspect-outgoing-network-call) to determine if the Google OAuth 2.0
[authorization requests](https://developers.google.com/identity/protocols/oauth2/native-app.md.txt#step-2:-send-a-request-to-googles-oauth-2.0-server) your app makes will cause the
granular permissions consent screen to be shown.

#### Inspect your application code

Review the sections of your application code where you are making calls to the
Google OAuth authorization endpoints to request permission from users. If you
use one of the Google API Client Libraries, you can often find what scopes your
application requests in the client initialization steps. Some examples are shown
in the following section. You should refer to the documentation of the SDKs your
application uses to handle Google OAuth 2.0 to determine if your application is
affected, using the guidance shown in the following examples as a reference.

### Google Identity Services

The following [Google Identity Services](https://developers.google.com/identity/oauth2/web/guides/overview.md.txt) JavaScript library
code snippet initializes the `TokenClient` with multiple non-Sign-In scopes.
The granular permission consent screen would be displayed when the web app
requests authorization from users.

```javascript
const client = google.accounts.oauth2.initTokenClient({
  client_id: 'YOUR_CLIENT_ID',
  scope: 'https://www.googleapis.com/auth/calendar.readonly \
  https://www.googleapis.com/auth/contacts.readonly',
  callback: (response) => {
    ...
  },
});
```

### Python

The following code snippet uses the `google-auth-oauthlib.flow` module to
construct the authorization request; The `scope` parameter includes two
non-Sign-In scopes. The granular permission consent screen would be
displayed when the web application requests authorization from users.

```python
import google.oauth2.credentials
import google_auth_oauthlib.flow

# Use the client_secret.json file to identify the application requesting
# authorization. The client ID (from that file) and access scopes are
# required.
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
    'client_secret.json',
    scopes=[
        'https://www.googleapis.com/auth/calendar.readonly',
        'https://www.googleapis.com/auth/contacts.readonly'
    ])
```

### Node.js

The code snippet following creates a `google.auth.OAuth2` object, which
defines the parameters in the authorization request whose `scope` parameter
includes two non-Sign-In scopes. The granular permission consent screen
would display when the web app requests authorization from users.

```javascript
const {google} = require('googleapis');

/**
  * To use OAuth2 authentication, we need access to a CLIENT_ID,
  * CLIENT_SECRET, AND REDIRECT_URI from the client_secret.json file.
  * To get these credentials for your application, visit
  * https://console.cloud.google.com/apis/credentials.
  */
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for read-only Calendar and Contacts.
const scopes = [
  'https://www.googleapis.com/auth/calendar.readonly',
  'https://www.googleapis.com/auth/contacts.readonly']
];

// Generate a url that asks permissions
const authorizationUrl = oauth2Client.generateAuthUrl({
  // 'online' (default) or 'offline' (gets refresh_token)
  access_type: 'offline',
  /** Pass in the scopes array defined earlier.
    * Alternatively, if only one scope is needed, you can pass a scope URL
    * as a string */
  scope: scopes,
  // Enable incremental authorization. Recommended as best practices.
  include_granted_scopes: true
});
```

#### Inspect outgoing network call

The method for inspecting network calls will vary depending on your application
type.

- **Web application** - [inspect network activity on Chrome](https://developer.chrome.com/docs/devtools/network/)
- **Android** - [inspect network traffic with the Network
  Inspector](https://developer.android.com/studio/debug/network-profiler#:%7E:text=From%20the%20Android%20Studio%20navigation,Network%20Inspector%20from%20the%20tabs.)
- **Chrome apps**
  - Navigate to the [Chrome extensions page](https://support.google.com/chrome_webstore/answer/2664769)
  - Check the **Developer mode** checkbox at the top right corner of the extension page
  - Select the extension you want to monitor
  - Click the **background page** link in the **Inspect views** section of the extension page
  - A **Developer Tools** dialog will open up where you can monitor the network traffic in the [Network tab](https://developer.chrome.com/docs/devtools/network/#load)
- **iOS** - [Analyzing HTTP traffic with Instruments](https://developer.apple.com/documentation/foundation/url_loading_system/analyzing_http_traffic_with_instruments)
- **Desktop apps** - [use a network capture tool](https://en.wikipedia.org/wiki/Comparison_of_packet_analyzers) available for the operating system the app was developed for

While inspecting network calls, look for requests sent to the Google OAuth
[authorization endpoints](https://developers.google.com/identity/protocols/oauth2/native-app.md.txt#step-2:-send-a-request-to-googles-oauth-2.0-server) and examine the `scope` parameter.

These values **cause** the granular permissions consent screen to be shown.

- The `scope` parameter contains Sign-In scopes and non-Sign-In scopes.

  A following sample request contains all three Sign-In scopes and one
  non-Sign-In scope to view the metadata of user's Google Drive files:

  ```
  https://accounts.google.com/o/oauth2/v2/auth?
  access_type=offline&
  scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile%20openid%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly&
  include_granted_scopes=true&
  response_type=code&
  redirect_uri=YOUR_REDIRECT_URL&
  client_id=YOUR_CLIENT_ID
  ```
- The `scope` parameter contains more than one non-Sign-In scope.

  A following sample request contains two non-Sign-In scopes to view the
  user's Google Drive metadata and manage specific Google Drive files:

  ```
  https://accounts.google.com/o/oauth2/v2/auth?
  access_type=offline&
  scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.metadata.readonly%20https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive.file&
  include_granted_scopes=true&
  response_type=code&
  redirect_uri=YOUR_REDIRECT_URL&
  client_id=YOUR_CLIENT_ID
  ```

## Best practices to handle granular permissions

If you [determine](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions.md.txt#affected) that your application needs to be updated to
handle granular permissions, you should make necessary updates to your code to
properly handle consent for multiple scopes. All applications should comply with
the following best practices:

1. **Review** the [Google API Services: User Data Policy](https://developers.google.com/terms/api-services-user-data-policy.md.txt) and make sure you comply with them.
2. **Request** specific scopes that are needed for a task. You must comply with the Google OAuth 2.0 policy that you [only request scopes that you
   need](https://developers.google.com/identity/protocols/oauth2/policies#scope-requests). You should avoid asking for multiple scopes at sign-in, unless it is essential for the core functionality of your app. Bundling several scopes together, especially for the first-time users unfamiliar with your application's features, can make it challenging for them to comprehend the need for these permissions. This may raise alarms and deter users from further engaging with your application.
3. **Provide** justification to users before asking the authorization request. Clearly explain why your application needs the requested permission, what you'll do with the user's data, and how the user will benefit from approving the request. Our research indicates that these explanations increase user trust and engagement.
4. **Use** [incremental authorization](https://developers.google.com/identity/protocols/oauth2/web-server.md.txt#incrementalAuth) whenever your application requests scopes to avoid having to manage multiple access tokens.
5. **Check** which scopes users granted. When requesting multiple scopes at once, users may not grant all scopes your app requests. Your app should always check which scopes were granted by the user and handle any denial of scopes by disabling relevant features. Follow the Google OAuth 2.0 policies on [handling consent for multiple scopes](https://developers.google.com/identity/protocols/oauth2/policies.md.txt#unbundled-consent) and only prompt the user for consent again once they have clearly indicated an intent to use the specific feature that requires the scope.

## Update your application to handle granular permissions

### Android applications

You should consult the documentation of SDKs you use to interact with Google
OAuth 2.0 and update your application to handle granular permissions based on
[best practices](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions.md.txt#best-practices-to-handle-granular-permissions).

If you use [auth.api.signin](https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignIn) SDK from Play Services to
interact with Google OAuth 2.0, you can use
[`requestPermissions`](https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignIn#public-static-void-requestpermissions-activity-activity,-int-requestcode,-googlesigninaccount-account,-scope...-scopes) function to request [the
smallest set of scopes needed](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions#best_practices_scope), and the
[`hasPermissions`](https://developers.google.com/android/reference/com/google/android/gms/auth/api/signin/GoogleSignIn#public-static-boolean-haspermissions-googlesigninaccount-account,-scope...-scopes) function to
[check](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions.md.txt#best_practices_granted) which scopes the user granted when requesting
granular permissions.

### Chrome extension applications

You should use [Chrome Identity API](https://developer.chrome.com/docs/extensions/mv3/tut_oauth/) to work with Google
OAuth 2.0 based on [best
practices](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions#best-practices-to-handle-granular-permissions).

The following example shows how to properly handle granular permissions.

**manifest.json**

The example manifest file declares two non-Sign-In scopes for the Chrome
extension application.

```
{
  "name": "Example Chrome extension application",
  ...
  "permissions": [
      "identity"
    ],
  "oauth2" : {
      "client_id": "YOUR_CLIENT_ID",
      "scopes":[
        "https://www.googleapis.com/auth/calendar.readonly",
        "https://www.googleapis.com/auth/contacts.readonly"
      ]
  }
}
```

#### Incorrect Approach

### All or nothing

Users click the button to initiate the authorization process. The code
snippet assumes users are presented with an "all-or-nothing" consent screen
for the two scopes specified in `manifest.json` file. It neglects to check
which scopes users granted.

**oauth.js**

```javascript
...
document.querySelector('button').addEventListener('click', function () {
  chrome.identity.getAuthToken({ interactive: true },
      function (token) {
          if (token === undefined) {
            // User didn't authorize both scopes.
            // Updating the UX and application accordingly
            ...
          } else {
            // User authorized both or one of the scopes.
            // It neglects to check which scopes users granted and assumes
            // users granted all scopes.

            // Calling the APIs, etc.
            ...
          }
      });
});
```

#### Correct Approach

### Smallest scopes

#### Select the smallest set of scopes needed

Application should only request the smallest set of scopes needed. It is
recommended that your application requests one scope at a time when it is
needed to complete a task.

In this example, it is assumed that both scopes declared in the
`manifest.json` file are the smallest set of scopes needed. The `oauth.js`
file uses Chrome Identity API to initiate the authorization process with
Google. You should opt in to [enable granular
permissions](https://developer.chrome.com/docs/extensions/reference/api/identity#type-TokenDetails), so users have greater control granting
permissions to your application. Your application should properly handle the
response from users by checking which scopes users authorize.

**oauth.js**

```javascript
...
document.querySelector('button').addEventListener('click', function () {
  chrome.identity.getAuthToken(
      { interactive: true, enableGranularPermissions: true },
      function (token, grantedScopes) {
          if (token === undefined) {
            // User didn't authorize any scope.
            // Updating the UX and application accordingly
            ...
          } else {
            // User authorized the request. Now, check which scopes were
            // granted.
            if (grantedScopes.includes(
                'https://www.googleapis.com/auth/calendar.readonly'))
            {
              // User authorized Calendar read permission.
              // Calling the APIs, etc.
              ...
            }
            else
            {
              // User didn't authorize Calendar read permission.
              // Update UX and application accordingly
              ...
            }

            if (grantedScopes.includes(
                'https://www.googleapis.com/auth/contacts.readonly'))
            {
              // User authorized Contacts read permission.
              // Calling the APIs, etc.
              ...
            }
            else
            {
              // User didn't authorize Contacts read permission.
              // Update UX and application accordingly
              ...
            }
          }
      });
});
```

### iOS, iPadOS, and macOS applications

You should consult the documentation of SDKs you use to interact with Google
OAuth 2.0 and update your application to handle granular permissions based on
[best practices](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions.md.txt#best-practices-to-handle-granular-permissions).

If you use the [Google Sign-In for iOS and macOS](https://developers.google.com/identity/sign-in/ios/start) library to interact
with Google OAuth 2.0, you should review the [documentation](https://developers.google.com/identity/sign-in/ios/api-access.md.txt)
on handling granular permissions.

### Web applications

You should consult the documentation of SDKs you use to interact with Google
OAuth 2.0 and update your application to handle granular permissions based on
[best practices](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions.md.txt#best-practices-to-handle-granular-permissions).

#### Server-side (offline) access

The server-side (offline) access mode requires you to do the following:

- Stand up a server and define a publicly accessible endpoint to receive the authorization code.
- Configure the [redirect URI](https://support.google.com/cloud/answer/6158849#zippy=%2Cweb-applications) of your public endpoint in the [Clients page](https://console.developers.google.com/auth/clients) of the Google Cloud console.

The following code snippet shows a NodeJS example requests two non-Sign-In
scopes. Users will see the granular permission consent screen.

##### Incorrect Approach

### All or nothing

Users are redirected to authorization URL. The code snippet assumes users
are presented with an "all-or-nothing" consent screen for the two scopes
specified in the `scopes` array. It neglects to check which scopes users
granted.

**main.js**

```javascript
...
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for two non-Sign-In scopes - Google Calendar and Contacts
const scopes = [
  'https://www.googleapis.com/auth/contacts.readonly',
  'https://www.googleapis.com/auth/calendar.readonly'
];

// Generate a url that asks permissions for the Google Calendar and Contacts
// scopes
const authorizationUrl = oauth2Client.generateAuthUrl({
  // 'online' (default) or 'offline' (gets refresh_token)
  access_type: 'offline',
  // Pass in the scopes array defined earlier
  scope: scopes,
  // Enable incremental authorization. Recommended as best practices.
  include_granted_scopes: true
});

async function main() {
  const server = http.createServer(async function (req, res) {
    // Example on redirecting user to the Google authorization server.
    if (req.url == '/') {
      res.writeHead(301, { "Location": authorizationUrl });
    }
    // Receive the callback from the Google authorization server.
    if (req.url.startsWith('/oauth2callback')) {
      // Handle the Google authorization server response
      let q = url.parse(req.url, true).query;

      if (q.error) {
        // User didn't authorize both scopes.
        // Updating the UX and application accordingly
        ...
      } else {
        // User authorized both or one of the scopes.
        // It neglects to check which scopes users granted and assumes users
        // granted all scopes.

        // Get access and refresh tokens (if access_type is offline)
        let { tokens } = await oauth2Client.getToken(q.code);
        // Calling the APIs, etc.
        ...
      }
    }
    res.end();
  }).listen(80);
}
```

##### Correct Approach

### Smallest scope

#### Select the smallest set of scopes needed

Application should only request the smallest set of scopes needed. It is
recommended that your application requests one scope at a time when it is
needed to complete a task. Whenever your application requests scopes, it
should use [incremental authorization](https://developers.google.com/identity/protocols/oauth2/web-server.md.txt#incrementalAuth) to avoid having to
manage multiple access tokens.

If your application must request multiple non-Sign-In scopes, you should
always use [incremental authorization](https://developers.google.com/identity/protocols/oauth2/web-server.md.txt#incrementalAuth) when requesting and
check which scopes users granted.

In this example, it is assumed that both scopes stated are required for the
app to properly function. You should opt in to [enable granular
permissions](https://developers.google.com/identity/protocols/oauth2/web-server#request-parameter-enable_granular_consent), so users have greater control
granting permissions to your application. Your application should properly
handle the response from users by checking which scopes they have
authorized.

**main.js**

```javascript
...
const oauth2Client = new google.auth.OAuth2(
  YOUR_CLIENT_ID,
  YOUR_CLIENT_SECRET,
  YOUR_REDIRECT_URL
);

// Access scopes for two non-Sign-In scopes - Google Calendar and Contacts
const scopes = [
  'https://www.googleapis.com/auth/contacts.readonly',
  'https://www.googleapis.com/auth/calendar.readonly'
];

// Generate a url that asks permissions for the Google Calendar and Contacts
// scopes
const authorizationUrl = oauth2Client.generateAuthUrl({
  // 'online' (default) or 'offline' (gets refresh_token)
  access_type: 'offline',
  // Pass in the scopes array defined earlier
  scope: scopes,
  // Enable incremental authorization. Recommended as best practices.
  include_granted_scopes: true,
  // Set to true to enable more granular permissions for Google OAuth 2.0
  // client IDs created before 2019. No effect for newer Google OAuth 2.0
  // client IDs, since more granular permissions is always enabled for them.
  enable_granular_consent: true
});

async function main() {
  const server = http.createServer(async function (req, res) {
    // Redirect users to the Google authorization server.
    if (req.url == '/') {
      res.writeHead(301, { "Location": authorizationUrl });
    }
    // Receive the callback from the Google authorization server.
    if (req.url.startsWith('/oauth2callback')) {
      // Handle the Google authorization server response
      let q = url.parse(req.url, true).query;

      if (q.error) {
        // User didn't authorize both scopes.
        // Updating the UX and application accordingly
        ...
      } else {
        // Get access and refresh tokens (if access_type is offline)
        let { tokens } = await oauth2Client.getToken(q.code);
        oauth2Client.setCredentials(tokens);

        // User authorized the request. Now, check which scopes were
        // granted.
        if (tokens.scope.includes(
            'https://www.googleapis.com/auth/calendar.readonly'))
        {
          // User authorized Calendar read permission.
          // Calling the APIs, etc.
          ...
        }
        else
        {
          // User didn't authorize Calendar read permission.
          // Calling the APIs, etc.
          ...
        }

        // Check which scopes user granted the permission to application
        if (tokens.scope.includes(
            'https://www.googleapis.com/auth/contacts.readonly'))
        {
          // User authorized Contacts read permission.
          // Calling the APIs, etc.
          ...
        }
        else
        {
          // User didn't authorize Contacts read permission.
          // Update UX and application accordingly
          ...
        }
      }
    }
    res.end();
  }).listen(80);
}
```

Review the [server-side web app guide](https://developers.google.com/identity/protocols/oauth2/web-server.md.txt) on how to access Google
APIs from server-based applications.

#### Client-side only access

- For applications that use [Google Identity Services](https://developers.google.com/identity/oauth2/web/guides/overview.md.txt) JavaScript library to interact with Google OAuth 2.0, you should review this [documentation](https://developers.google.com/identity/oauth2/web/guides/use-token-model.md.txt#granular_permissions) on handling granular permissions.
- For applications that directly make calls using JavaScript to Google OAuth 2.0 authorization endpoints, you should review this [documentation](https://developers.google.com/identity/protocols/oauth2/javascript-implicit-flow.md.txt#incrementalAuth) on handling granular permissions.

## Test your updated application on handling granular permissions

1. **Outline** all the cases that users can respond to permission requests and the expected behavior from your application. For example, if the user only authorizes two out of three requested scopes, your application should behave accordingly.
2. **Test** your application with granular permission enabled. There are two
   ways to enable granular permissions:

   1. Check the OAuth 2.0 consent screens of your application to see if [granular permissions](https://developers.google.com/identity/protocols/oauth2/resources/granular-permissions.md.txt#granular-applicable) are already enabled for your application. You can also create a new Web, Android, or iOS Google OAuth 2.0 client ID through the Google Cloud console for testing purposes as granular permission is always enabled for them.
   2. Set the parameter [`enable_granular_consent`](https://developers.google.com/identity/protocols/oauth2/web-server.md.txt#request-parameter-enable_granular_consent)
      to `true` when calling the Google OAuth [authorization
      endpoints](https://developers.google.com/identity/protocols/oauth2/native-app#step-2:-send-a-request-to-googles-oauth-2.0-server). Some SDKs have explicit support for this
      parameter. For others, check the documentation to see how you can add
      this parameter and its value manually. If your implementation doesn't
      support adding the parameter, you can create a new Web, Android, or iOS
      Google OAuth 2.0 client ID through the Google Cloud console for testing
      purposes only as stated in preceding point.

      > [!IMPORTANT]
      > **Important:** When Google enables granular permissions for an application, the `enable_granular_consent` parameter will no longer have any effect.

3. When testing your updated application, use a personal Google Account
   (@gmail.com) instead of a Workspace account. This is because Workspace
   Enterprise apps with [domain-wide delegation of
   authority](https://support.google.com/a/answer/162106) or marked as [Trusted](https://support.google.com/a/answer/7281227#zippy=%2Cchange-access-from-the-app-list) are
   not affected by the changes to granular permissions at this time. Therefore,
   testing with a Workspace account from your organization might not show the
   new granular consent screen as intended.