세분화된 권한 처리 방법

개요

세분화된 권한을 사용하면 소비자가 각 앱과 공유할 계정 데이터를 더 세밀하게 제어할 수 있습니다. 세분화된 권한은 더 많은 제어, 투명성, 보안을 제공하여 사용자와 개발자 모두에게 이점을 제공합니다. 이 가이드는 세분화된 권한을 처리하도록 애플리케이션을 업데이트하는 데 필요한 변경사항과 단계를 이해하는 데 도움이 됩니다.

세분화된 권한이란 무엇인가요?

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

로그인 범위가 아닌 범위가 두 개 이상 요청되는 경우

로그인 범위 및 로그인 범위가 아닌 범위

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

로그인 및 비로그인 범위

로그인 범위가 아닌 범위가 두 개 이상인 경우

애플리케이션이 로그인 범위가 아닌 범위를 두 개 이상 요청하면 사용자에게 세분화된 권한 동의 화면이 표시됩니다. 사용자는 애플리케이션과 공유하기 위해 승인할 권한을 선택할 수 있습니다. 다음은 사용자의 Gmail 메시지 및 Google Calendar 데이터에 대한 액세스를 요청하는 세분화된 권한 동의 화면의 예입니다.

로그인하지 않은 범위가 두 개 이상

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

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

로그인 범위 수 로그인 범위가 아닌 범위 수 세분화된 권한 동의 화면
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 서비스 JavaScript 라이브러리 코드 스니펫은 여러 로그인 범위가 아닌 범위로 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 매개변수에는 로그인 범위가 아닌 범위가 두 개 포함되어 있습니다. 웹 애플리케이션이 사용자에게 승인을 요청하면 세분화된 권한 동의 화면이 표시됩니다.

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 매개변수에는 로그인 범위가 아닌 범위가 두 개 포함되어 있습니다. 웹 앱이 사용자에게 승인을 요청하면 세분화된 권한 동의 화면이 표시됩니다.

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 매개변수를 검사합니다.

이러한 값으로 인해 세분화된 권한 동의 화면이 표시됩니다.

  • scope 매개변수에는 로그인 범위와 로그인 범위가 아닌 범위가 포함되어 있습니다.

    다음 샘플 요청에는 세 가지 로그인 범위와 사용자의 Google Drive 파일의 메타데이터를 볼 수 있는 로그인 범위가 아닌 범위가 하나 포함되어 있습니다.

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

    다음 샘플 요청에는 사용자의 Google Drive 메타데이터를 보고 특정 Google Drive 파일을 관리할 수 있는 로그인 범위가 아닌 범위가 두 개 포함되어 있습니다.

  • 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

세분화된 권한을 처리하기 위한 권장사항

세분화된 권한을 처리하도록 애플리케이션을 업데이트해야 한다고 판단되면 여러 범위에 대한 동의를 적절히 처리하도록 코드를 업데이트해야 합니다. 모든 애플리케이션은 다음 권장사항을 준수해야 합니다.

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

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

Android 애플리케이션

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

auth.api.signin

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

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

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

manifest.json

매니페스트 파일 예에서는 Chrome 확장 프로그램 애플리케이션에 대해 로그인 범위가 아닌 범위를 두 개 선언합니다.

{
  "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 ID 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의 문서를 참조하고 권장사항에 따라 세분화된 권한을 처리하도록 애플리케이션을 업데이트해야 합니다.

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

웹 애플리케이션

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

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

서버 측 (오프라인) 액세스 모드를 사용하려면 다음 작업을 실행해야 합니다.
  • 서버를 설정하고 승인 코드를 수신할 공개적으로 액세스 가능한 엔드포인트를 정의합니다.
  • Google Cloud 콘솔의 클라이언트 페이지에서 공개 엔드포인트의 리디렉션 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에 액세스하는 방법에 관한 서버 측 웹 앱 가이드를 검토합니다.

클라이언트 측 전용 액세스

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

  1. 사용자가 권한 요청에 응답할 수 있는 모든 사례와 애플리케이션의 예상 동작을 간략하게 설명 합니다. 예를 들어 사용자가 요청된 세 범위 중 두 범위만 승인하는 경우 애플리케이션은 그에 따라 동작해야 합니다.
  2. 세분화된 권한이 사용 설정된 상태로 애플리케이션을 테스트 합니다. 세분화된 권한을 사용 설정하는 방법은 두 가지입니다.
    1. 애플리케이션의 OAuth 2.0 동의 화면을 확인하여 세분화된 권한이 애플리케이션에 이미 사용 설정되어 있는지 확인합니다. 세분화된 권한은 항상 사용 설정되어 있으므로 테스트 목적으로 Google Cloud 콘솔을 통해 새 웹, Android 또는 iOS Google OAuth 2.0 클라이언트 ID 를 만들 수도 있습니다.
    2. Google OAuth 승인 엔드포인트를 호출할 때 매개변수 enable_granular_consenttrue로 설정합니다. 일부 SDK는 이 매개변수를 명시적으로 지원합니다. 다른 SDK의 경우 문서를 확인하여 이 매개변수와 값을 수동으로 추가하는 방법을 알아보세요. 구현에서 매개변수 추가를 지원하지 않는 경우 이전 항목에 설명된 대로 테스트 목적으로만 Google Cloud 콘솔을 통해 새 웹, Android 또는 iOS Google OAuth 2.0 클라이언트 ID를 만들 수 있습니다.
  3. 업데이트된 애플리케이션을 테스트할 때는 Workspace 계정 대신 개인 Google 계정 (@gmail.com)을 사용합니다. 이는 도메인 전체 권한 위임이 있거나 신뢰할 수 있는 것으로 표시된 Workspace Enterprise 앱은 현재 세분화된 권한 변경사항의 영향을 받지 않기 때문입니다. 따라서 조직의 Workspace 계정으로 테스트하면 새 세분화된 동의 화면이 의도한 대로 표시되지 않을 수 있습니다.