세분화된 권한 처리 방법

개요

세분화된 권한을 통해 소비자는 각 앱과 공유할 계정 데이터를 더 세밀하게 제어할 수 있습니다. 또한 더 우수한 제어 기능, 투명성 및 보안이 제공되므로 사용자와 개발자 모두에게 도움이 됩니다. 이 가이드에서는 세분화된 권한을 처리하도록 애플리케이션을 성공적으로 업데이트하는 데 필요한 변경사항과 단계를 설명합니다.

상세 권한이란 무엇인가요?

이메일과 캘린더 범위를 모두 요청하는 생산성 앱을 개발한다고 가정해 보겠습니다. 사용자는 애플리케이션을 Google Calendar에서만 사용하고 Gmail에서는 사용하지 않기를 원할 수 있습니다. 세분화된 OAuth 권한을 통해 사용자는 Google Calendar 권한만 부여하고 Gmail은 부여하지 않도록 선택할 수 있습니다. 사용자가 특정 데이터에 대한 액세스 권한을 부여할 수 있도록 하면 데이터 노출을 최소화하고 신뢰도를 높이며 개인 정보 보호를 최우선으로 하여 디지털 생활을 제어할 수 있습니다. 이러한 시나리오를 처리하도록 애플리케이션을 설계하는 것이 중요합니다.

로그인이 아닌 범위가 2개 이상 요청된 경우

로그인 및 로그인 이외 범위

로그인 범위와 비로그인 범위를 모두 요청하는 애플리케이션의 경우 사용자에게 먼저 로그인 범위(email, profile, openid)의 동의 페이지가 표시됩니다. 사용자가 기본 ID 정보 (이름, 이메일 주소, 프로필 사진)를 공유하는 데 동의하면 로그인 이외 범위에 대한 상세 권한 동의 화면이 사용자에게 표시됩니다. 이 경우 애플리케이션은 사용자가 부여한 범위를 확인해야 하며 사용자가 요청된 모든 범위를 부여한다고 가정해서는 안 됩니다. 다음 예에서 웹 애플리케이션은 세 개의 로그인 범위와 하나의 Google 드라이브 비로그인 범위를 모두 요청합니다. 사용자가 로그인 범위에 동의하면 Google Drive 권한에 대한 상세 권한 동의 화면이 표시됩니다.

로그인 및 로그인 이외 범위

로그인 이외 범위 2개 이상

애플리케이션이 로그인 외 범위를 2개 이상 요청하는 경우 세부적인 권한 동의 화면이 사용자에게 표시됩니다. 사용자는 애플리케이션과 공유하도록 승인할 권한을 선택할 수 있습니다. 다음은 사용자의 Gmail 메시지 및 Google Calendar 데이터에 대한 액세스를 요청하는 상세 권한 동의 화면의 예입니다.

로그인 이외 범위 2개 이상

로그인 범위 (email, profile, openid)만 요청하는 애플리케이션의 경우 #inspect-your-application-codegranular 권한 동의 화면이 적용되지 않습니다. 사용자는 전체 로그인 요청을 승인하거나 거부합니다. 즉, 애플리케이션이 로그인 범위 (하나, 두 개 또는 세 개 모두)만 요청하는 경우 세분화된 권한 동의 화면을 적용할 수 없습니다.

로그인 이외의 범위를 하나만 요청하는 애플리케이션의 경우 세분화된 권한 동의 화면이 적용되지 않습니다. 즉, 사용자가 전체 요청을 승인하거나 거부하며 동의 화면에 체크박스가 없습니다. 다음 표에는 상세한 권한 동의 화면이 표시되는 시점이 요약되어 있습니다.

로그인 범위 수 로그인이 아닌 범위 수 상세 권한 동의 화면
1-3 0 해당 사항 없음
1-3 1명 이상 적용 가능
0 1 해당 사항 없음
0 2+ 적용 가능

애플리케이션이 영향을 받는지 확인

권한 요청에 Google OAuth 2.0 승인 엔드포인트가 사용되는 애플리케이션의 모든 섹션을 철저히 검토합니다. 여러 범위를 요청하는 사용자는 사용자에게 표시되는 세분화된 권한 동의 화면을 활성화할 때 주의해야 합니다. 이 경우 사용자가 일부 범위만 승인하는 경우를 코드에서 처리할 수 있어야 합니다.

애플리케이션이 여러 범위를 사용하는지 확인하는 방법

앱 코드를 검사하거나 발신 네트워크 호출을 검사하여 앱에서 Google OAuth 2.0 승인 요청을 할 때 세분화된 권한 동의 화면이 표시되는지 확인합니다.

애플리케이션 코드 검사

Google OAuth 승인 엔드포인트를 호출하여 사용자에게 권한을 요청하는 애플리케이션 코드의 섹션을 검토합니다. Google API 클라이언트 라이브러리 중 하나를 사용하면 클라이언트 초기화 단계에서 애플리케이션이 요청하는 범위를 확인할 수 있는 경우가 많습니다. 몇 가지 예는 다음 섹션에 나와 있습니다. 다음 예시의 안내를 참조하여 애플리케이션에서 Google OAuth 2.0을 처리하는 데 사용하는 SDK의 문서를 참조하여 애플리케이션이 영향을 받는지 확인해야 합니다.

Google ID 서비스

다음 Google ID 서비스 자바스크립트 라이브러리 코드 스니펫은 로그인 이외의 여러 범위를 사용하여 TokenClient를 초기화합니다. 웹 앱에서 사용자에게 승인을 요청하면 세분화된 권한 동의 화면이 표시됩니다.

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

다음 코드 스니펫은 google-auth-oauthlib.flow 모듈을 사용하여 승인 요청을 구성합니다. scope 매개변수에는 로그인 이외의 범위 2개가 포함됩니다. 웹 애플리케이션이 사용자에게 승인을 요청하면 세분화된 권한 동의 화면이 표시됩니다.

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

다음 코드 스니펫은 google.auth.OAuth2 객체를 만듭니다. 이 객체는 scope 매개변수에 로그인 이외의 범위 2개가 포함된 승인 요청의 매개변수를 정의합니다. 웹 앱에서 사용자에게 승인을 요청하면 세분화된 권한 동의 화면이 표시됩니다.

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 above.
    * 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
});

발신 네트워크 호출 검사

네트워크 호출을 검사하는 메서드는 애플리케이션 클라이언트 유형에 따라 다릅니다.

네트워크 호출을 검사하는 동안 Google OAuth 승인 엔드포인트로 전송된 요청을 찾아 scope 매개변수를 검사합니다.

이러한 값을 사용하면 세분화된 권한 동의 화면이 cause.

  • scope 매개변수에는 로그인 범위와 비로그인 범위가 포함됩니다.

    다음 샘플 요청에는 사용자의 Google 드라이브 파일의 메타데이터를 보기 위한 3개의 로그인 범위와 1개의 비로그인 범위가 모두 포함되어 있습니다.

    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
  • scope 매개변수에 로그인이 아닌 범위가 2개 이상 포함되어 있습니다.

    다음 샘플 요청에는 사용자의 Google 드라이브 메타데이터를 보고 특정 Google 드라이브 파일을 관리하기 위한 로그인 이외의 범위 2개가 포함되어 있습니다.

  • 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

세분화된 권한 처리를 위한 권장사항

세분화된 권한을 처리하기 위해 애플리케이션을 업데이트해야 한다고 determine한 경우 여러 범위에 대한 동의를 올바르게 처리할 수 있도록 코드를 업데이트해야 합니다. 모든 애플리케이션은 다음 권장사항을 준수해야 합니다.

  1. Google API 서비스: 사용자 데이터 정책검토하고 이를 준수하는지 확인합니다.
  2. 작업에 필요한 특정 범위를 요청합니다. 필요한 범위만 요청하는 Google OAuth 2.0 정책을 준수해야 합니다. 앱의 핵심 기능에 꼭 필요한 경우가 아니라면 로그인 시 다중 범위를 요청하지 말아야 합니다. 특히 애플리케이션의 기능에 익숙하지 않은 최초 사용자가 여러 범위를 함께 번들로 묶으면 이러한 권한의 필요성을 이해하기 어려울 수 있습니다. 이렇게 하면 경보가 울리고 사용자가 애플리케이션을 더 이상 사용하지 않을 수 있습니다.
  3. 승인 요청을 요청하기 전에 사용자에게 이유를 제공합니다. 애플리케이션에 요청된 권한이 필요한 이유, 사용자 데이터를 어떻게 처리할지, 요청을 승인함으로써 사용자가 어떤 이점을 얻을 수 있는지 명확하게 설명합니다. 연구에 따르면 이러한 설명이 사용자 신뢰와 참여도를 높이는 것으로 나타났습니다.
  4. 애플리케이션에서 범위를 요청할 때마다 증분 승인사용하여 여러 개의 액세스 토큰을 관리할 필요가 없도록 합니다.
  5. 사용자가 부여한 범위를 확인합니다. 한 번에 여러 범위를 요청할 때 사용자가 앱에서 모든 범위를 요청하지 못할 수 있습니다. 앱은 항상 사용자가 부여한 범위를 확인하고 관련 기능을 사용 중지하여 범위 거부를 처리해야 합니다. 여러 범위에 대한 동의 처리에 관한 Google OAuth 2.0 정책을 따르고, 범위가 필요한 특정 기능을 사용하겠다는 의도를 명확히 한 경우에만 사용자에게 동의를 요청하는 메시지를 다시 표시합니다.

세분화된 권한을 처리하도록 애플리케이션 업데이트

Android 애플리케이션

Google OAuth 2.0과 상호작용하는 데 사용하는 SDK 문서를 참고하고 권장사항에 따라 세분화된 권한을 처리하도록 애플리케이션을 업데이트해야 합니다.

Play 서비스의 auth.api.signin SDK를 사용하여 Google OAuth 2.0과 상호작용하는 경우 requestPermissions 함수를 사용하여 필요한 가장 작은 범위 집합을 요청하고 hasPermissions 함수를 사용하여 세분화된 권한을 요청할 때 사용자가 부여한 범위를 확인할 수 있습니다.

Chrome 확장 프로그램 애플리케이션

권장사항에 따라 Google OAuth 2.0을 사용하려면 Chrome Identity API를 사용해야 합니다.

다음 예에서는 세분화된 권한을 올바르게 처리하는 방법을 보여줍니다.

manifest.json

예시 매니페스트 파일은 Chrome 확장 프로그램 애플리케이션에 로그인이 아닌 범위 2개를 선언합니다.

{
  "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"]
  }
}

잘못된 접근 방식

전체 또는 없음

사용자는 버튼을 클릭하여 승인 프로세스를 시작합니다. 이 코드 스니펫은 manifest.json 파일에 지정된 두 범위에 대해 '전부 아니면 없음' 동의 화면이 사용자에게 표시된다고 가정합니다. 사용자가 부여한 범위를 확인하지 않습니다.

oauth.js

...
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.
            ...
          }
      });
});

올바른 접근 방식

최소 범위

필요한 최소 범위 집합 선택

애플리케이션은 필요한 최소 범위 집합만 요청해야 합니다. 애플리케이션에서 작업을 완료하는 데 필요할 때 한 번에 하나의 범위를 요청하는 것이 좋습니다.

이 예에서는 manifest.json 파일에 선언된 두 범위가 필요한 최소 범위 집합이라고 가정합니다. oauth.js 파일은 Chrome Identity API를 사용하여 Google에서 승인 프로세스를 시작합니다. 사용자가 애플리케이션에 대한 권한 부여를 더 세밀하게 제어할 수 있도록 세분화된 권한을 사용 설정하도록 선택해야 합니다. 애플리케이션에서는 사용자가 승인하는 범위를 확인하여 사용자의 응답을 올바르게 처리해야 합니다.

oauth.js

...
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, macOS 애플리케이션

Google OAuth 2.0과 상호작용하는 데 사용하는 SDK 문서를 참고하고 권장사항에 따라 세분화된 권한을 처리하도록 애플리케이션을 업데이트해야 합니다.

iOS 및 macOS용 Google 로그인 라이브러리를 사용하여 Google OAuth 2.0과 상호작용하는 경우 세분화된 권한 처리에 관한 문서를 검토해야 합니다.

웹 애플리케이션

Google OAuth 2.0과 상호작용하는 데 사용하는 SDK 문서를 참고하고 권장사항에 따라 세분화된 권한을 처리하도록 애플리케이션을 업데이트해야 합니다.

서버 측 (오프라인) 액세스

서버 측 (오프라인) 액세스 모드를 사용하려면 다음을 실행해야 합니다.
  • 서버를 설정하고 승인 코드를 수신하기 위해 공개적으로 액세스할 수 있는 엔드포인트를 정의합니다.
  • Google Cloud 콘솔의 Credentials page 에서 공개 엔드포인트의 리디렉션 URI 를 구성합니다.

다음 코드 스니펫은 로그인이 아닌 두 범위를 요청하는 NodeJS 예시를 보여줍니다. 사용자에게 상세 권한 동의 화면이 표시됩니다.

잘못된 접근 방식

전체 또는 없음

사용자가 승인 URL로 리디렉션됩니다. 이 코드 스니펫은 scopes 배열에 지정된 두 범위에 대해 '전부 아니면 전무' 동의 화면이 사용자에게 표시되었다고 가정합니다. 사용자가 부여한 범위를 확인하지 않습니다.

main.js

...
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 above
  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 Google OAuth 2.0 server.
    if (req.url == '/') {
      res.writeHead(301, { "Location": authorizationUrl });
    }
    // Receive the callback from Google OAuth 2.0 server.
    if (req.url.startsWith('/oauth2callback')) {
      // Handle the Google OAuth 2.0 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);
}
올바른 접근 방식

최소 범위

필요한 최소 범위 집합 선택

애플리케이션은 필요한 최소 범위 집합만 요청해야 합니다. 애플리케이션에서 작업을 완료하는 데 필요할 때 한 번에 하나의 범위를 요청하는 것이 좋습니다. 애플리케이션은 범위를 요청할 때마다 증분 승인을 사용하여 여러 개의 액세스 토큰을 관리할 필요가 없도록 해야 합니다.

애플리케이션에서 로그인이 아닌 범위를 여러 개 요청해야 하는 경우 요청할 때 항상 증분 승인을 사용하고 사용자가 부여한 범위를 확인해야 합니다.

이 예에서는 앱이 제대로 작동하려면 명시된 두 범위가 모두 필요하다고 가정합니다. 사용자가 애플리케이션에 대한 권한 부여를 더 세밀하게 제어할 수 있도록 세분화된 권한을 사용 설정하도록 선택해야 합니다. 애플리케이션에서는 사용자가 승인한 범위를 확인하여 사용자의 응답을 올바르게 처리해야 합니다.

main.js

...
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 above
  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 Google OAuth 2.0 server.
    if (req.url == '/') {
      res.writeHead(301, { "Location": authorizationUrl });
    }
    // Receive the callback from Google OAuth 2.0 server.
    if (req.url.startsWith('/oauth2callback')) {
      // Handle the Google OAuth 2.0 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);
}

서버 기반 애플리케이션에서 Google API에 액세스하는 방법은 서버 측 웹 앱 가이드를 참고하세요.

클라이언트 측 전용 액세스

  • Google ID 서비스 JavaScript 라이브러리를 사용하여 Google OAuth 2.0과 상호작용하는 애플리케이션의 경우 세분화된 권한 처리에 관한 문서를 검토해야 합니다.
  • 자바스크립트를 사용하여 Google OAuth 2.0 승인 엔드포인트에 직접 호출하는 애플리케이션의 경우 세분화된 권한 처리에 관한 이 문서를 검토해야 합니다.

세분화된 권한 처리에 관해 업데이트된 애플리케이션 테스트

  1. 사용자가 권한 요청에 응답할 수 있는 모든 사례와 애플리케이션의 예상 동작을 개략적으로 설명합니다. 예를 들어 사용자가 3개의 요청된 범위 중 2개만 승인한 경우 애플리케이션이 이에 따라 작동해야 합니다.
  2. 세분화된 권한을 사용 설정하여 애플리케이션을 테스트합니다. 세분화된 권한을 사용 설정하는 방법에는 두 가지가 있습니다.
    1. 애플리케이션의 OAuth 2.0 동의 화면에서 세부 권한이 애플리케이션에 이미 사용 설정되어 있는지 확인합니다. 또한 세분화된 권한이 항상 사용 설정되어 있으므로 테스트 목적으로 Google Cloud 콘솔을 통해 새 웹, Android 또는 iOS Google OAuth 2.0 클라이언트 ID를 만들 수 있습니다.
    2. Google OAuth 승인 엔드포인트를 호출할 때 매개변수 enable_granular_consenttrue로 설정합니다. 일부 SDK는 이 매개변수를 명시적으로 지원합니다. 그 외의 경우 문서를 참조하여 이 매개변수와 값을 수동으로 추가하는 방법을 확인하세요. 구현에서 매개변수 추가를 지원하지 않는 경우 이전 포인트에서 설명한 대로 테스트 목적으로만 Google Cloud 콘솔을 통해 새로운 웹, Android 또는 iOS Google OAuth 2.0 클라이언트 ID를 만들 수 있습니다.