OAuth 2.0

이 문서에서는 OAuth 2.0, OAuth 2.0을 사용해야 하는 경우, 클라이언트 ID를 획득하는 방법, .NET용 Google API 클라이언트 라이브러리에서 OAuth 2.0을 사용하는 방법을 설명합니다.

OAuth 2.0 프로토콜

OAuth 2.0은 Google API에서 사용하는 승인 프로토콜입니다. 다음 링크를 읽고 프로토콜에 익숙해져야 합니다.

클라이언트 ID 및 보안 비밀 획득

Google API 콘솔에서 클라이언트 ID와 보안 비밀을 가져올 수 있습니다. 다양한 유형의 클라이언트 ID가 있으므로 애플리케이션에 맞는 유형을 가져와야 합니다.

아래의 각 코드 스니펫 (서비스 계정 제외)에서 클라이언트 보안 비밀번호를 다운로드하여 프로젝트에 client_secrets.json로 저장해야 합니다.

사용자 인증 정보

사용자 인증 정보

UserCredential는 액세스 토큰을 사용하여 보호된 리소스에 액세스하기 위한 스레드로부터 안전한 도우미 클래스입니다. 액세스 토큰은 일반적으로 1시간 후에 만료되며 그 후에 사용하려고 하면 오류가 발생합니다.

UserCredentialAuthorizationCodeFlow는 자동으로 토큰을 '새로고침'합니다. 즉, 새 액세스 토큰을 가져올 수 있습니다. 승인 코드 흐름 중에 access_type=offline 매개변수를 사용하는 경우 액세스 토큰과 함께 제공되는 장기 갱신 토큰을 사용하여 이 작업을 수행합니다.

대부분의 애플리케이션에서 사용자 인증 정보의 액세스 토큰과 갱신 토큰을 영구 저장소에 저장하는 것이 좋습니다. 그렇지 않으면 액세스 토큰이 수신되고 1시간 후에 만료되므로 매시간 브라우저에서 승인 페이지를 최종 사용자에게 제시해야 합니다.

액세스 토큰과 갱신 토큰이 유지되도록 하려면 IDataStore의 자체 구현을 제공하거나 라이브러리에서 제공하는 다음 구현 중 하나를 사용하면 됩니다.

  • .NET용 FileDataStore를 사용하면 사용자 인증 정보가 파일에 영구적으로 유지됩니다.

ServiceAccountCredential

ServiceAccountCredentialUserCredential와 비슷하지만 다른 용도로 사용됩니다. Google OAuth 2.0은 웹 애플리케이션과 Google Cloud Storage 간 상호작용과 같은 서버 간 상호작용을 지원합니다. 요청하는 애플리케이션은 API에 액세스하기 위해 자체 ID를 증명해야 하며 최종 사용자는 관여할 필요가 없습니다. ServiceAccountCredential는 새 액세스 토큰 가져오기 요청에 서명하는 데 사용되는 비공개 키를 저장합니다.

UserCredentialServiceAccountCredential 모두 IConfigurableHttpClientInitializer를 구현하므로 각각을 다음과 같이 등록할 수 있습니다.

  • 실패한 응답 핸들러입니다. HTTP 401 상태 코드를 수신하면 토큰을 새로고침합니다.
  • 모든 요청에서 Authorization 헤더를 가로채는 인터셉터.

설치된 애플리케이션

Books API를 사용하는 샘플 코드:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Books.v1;
using Google.Apis.Books.v1.Data;
using Google.Apis.Services;
using Google.Apis.Util.Store;

namespace Books.ListMyLibrary
{
    /// <summary>
    /// Sample which demonstrates how to use the Books API.
    /// https://developers.google.com/books/docs/v1/getting_started
    /// <summary>
    internal class Program
    {
        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("Books API Sample: List MyLibrary");
            Console.WriteLine("================================");
            try
            {
                new Program().Run().Wait();
            }
            catch (AggregateException ex)
            {
                foreach (var e in ex.InnerExceptions)
                {
                    Console.WriteLine("ERROR: " + e.Message);
                }
            }
            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }

        private async Task Run()
        {
            UserCredential credential;
            using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
            {
                credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.Load(stream).Secrets,
                    new[] { BooksService.Scope.Books },
                    "user", CancellationToken.None, new FileDataStore("Books.ListMyLibrary"));
            }

            // Create the service.
            var service = new BooksService(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName = "Books API Sample",
                });

            var bookshelves = await service.Mylibrary.Bookshelves.List().ExecuteAsync();
            ...
        }
    }
}
  
  • 이 샘플 코드에서는 GoogleWebAuthorizationBroker.AuthorizeAsync 메서드를 호출하여 새 UserCredential 인스턴스를 만듭니다. 이 정적 메서드는 다음을 가져옵니다.

    • 클라이언트 보안 비밀번호 (또는 클라이언트 보안 비밀번호에 대한 스트림)입니다.
    • 필수 범위입니다.
    • 사용자 식별자입니다.
    • 작업 취소를 위한 취소 토큰입니다.
    • 데이터 스토어(선택사항) 데이터 스토어가 지정되지 않은 경우 기본값은 기본 Google.Apis.Auth 폴더가 있는 FileDataStore입니다. 폴더가 Environment.SpecialFolder.ApplicationData에 생성됩니다.
  • 이 메서드에서 반환하는 UserCredential는 이니셜라이저를 사용하여 BooksService에서 HttpClientInitializer로 설정됩니다. 위에서 설명한 것처럼 UserCredentialHTTP 클라이언트 이니셜라이저를 구현합니다.

  • 위의 샘플 코드에서는 클라이언트 보안 비밀번호 정보가 파일에서 로드되지만 다음 작업도 수행할 수 있습니다.

    credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
        new ClientSecrets
        {
            ClientId = "PUT_CLIENT_ID_HERE",
            ClientSecret = "PUT_CLIENT_SECRETS_HERE"
        },
        new[] { BooksService.Scope.Books },
        "user",
        CancellationToken.None,
        new FileDataStore("Books.ListMyLibrary"));
          

도서 샘플을 살펴보세요.

웹 애플리케이션 (ASP.NET Core 3)

Google API는 웹 서버 애플리케이션용 OAuth 2.0을 지원합니다.

Google.Apis.Auth.AspNetCore3은 ASP.NET Core 3 애플리케이션의 Google 기반 OAuth 2.0 시나리오 대부분에 사용하기 위해 권장되는 라이브러리입니다. Google 전용 OpenIdConnect 인증 핸들러를 구현합니다. 증분 인증을 지원하며 삽입 가능한 IGoogleAuthProvider를 정의하여 Google API와 함께 사용할 수 있는 Google 사용자 인증 정보를 제공합니다.

이 섹션에서는 Google.Apis.Auth.AspNetCore3을 구성하고 사용하는 방법을 설명합니다. 여기에 표시된 코드는 완전히 작동하는 표준 ASP.NET Core 3 애플리케이션인 Google.Apis.Auth.AspNetCore3.IntegrationTests를 기반으로 합니다.

이 문서를 튜토리얼로 따라 진행하려면 자체 ASP.NET Core 3 애플리케이션이 필요하며 이 단계를 선행 조건으로 완료해야 합니다.

기본 요건

  • Google.Apis.Auth.AspNetCore3 패키지를 설치합니다.
  • Google Drive API를 사용하므로 Google.Apis.Drive.v3 패키지도 설치해야 합니다.
  • Google Cloud 프로젝트가 없으면 프로젝트를 만듭니다. 이렇게 하려면 이 안내를 따르세요. 이 프로젝트가 앱을 식별하는 프로젝트입니다.
  • Google Drive API를 사용 설정해야 합니다. API를 사용 설정하려면 이 안내를 따르세요.
  • Google에서 앱을 식별할 수 있는 승인 사용자 인증 정보를 만듭니다. 이 안내에 따라 승인 사용자 인증 정보를 만들고 client_secrets.json 파일을 다운로드합니다. 두 가지 주요 특징은 다음과 같습니다.
    • 사용자 인증 정보 유형은 웹 애플리케이션이어야 합니다.
    • 이 앱을 실행하려면 추가해야 하는 리디렉션 URI는 https://localhost:5001/signin-oidc뿐입니다.

Google.Apis.Auth.AspNetCore3을 사용하도록 애플리케이션 구성

Google.Apis.Auth.AspNetCore3은 Startup 클래스 또는 사용 중인 유사한 대안에서 구성됩니다. 다음 스니펫은 Google.Apis.Auth.AspNetCore3.IntegrationTests 프로젝트의 Startup.cs에서 추출되었습니다.

  • 다음 using 지시어를 Startup.cs 파일에 추가합니다.
    using Google.Apis.Auth.AspNetCore3;
  • Startup.ConfigureServices 메서드에서 다음 코드를 추가하고 클라이언트 ID 및 클라이언트 보안 비밀번호 자리표시자를 client_secrets.json 파일에 포함된 값으로 변경합니다. 이러한 값은 JSON 파일에서 직접 로드하거나 다른 안전한 방식으로 저장할 수 있습니다. JSON 파일에서 이러한 값을 직접 로드하는 방법의 예시는 Google.Apis.Auth.AspNetCore3.IntegrationTests 프로젝트의 ClientInfo.Load 메서드를 참조하세요.
    public void ConfigureServices(IServiceCollection services)
    {
        ...
    
        // This configures Google.Apis.Auth.AspNetCore3 for use in this app.
        services
            .AddAuthentication(o =>
            {
                // This forces challenge results to be handled by Google OpenID Handler, so there's no
                // need to add an AccountController that emits challenges for Login.
                o.DefaultChallengeScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
                // This forces forbid results to be handled by Google OpenID Handler, which checks if
                // extra scopes are required and does automatic incremental auth.
                o.DefaultForbidScheme = GoogleOpenIdConnectDefaults.AuthenticationScheme;
                // Default scheme that will handle everything else.
                // Once a user is authenticated, the OAuth2 token info is stored in cookies.
                o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddGoogleOpenIdConnect(options =>
            {
                options.ClientId = {YOUR_CLIENT_ID};
                options.ClientSecret = {YOUR_CLIENT_SECRET};
            });
    }
          
  • Startup.Configure 메서드에서 ASP.NET Core 3 인증 및 승인 미들웨어 구성요소와 HTTPS 리디렉션을 파이프라인에 추가해야 합니다.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...
        app.UseHttpsRedirection();
        ...
    
        app.UseAuthentication();
        app.UseAuthorization();
    
        ...
    }
          

사용자 인증 정보를 사용하여 사용자를 대신하여 Google API에 액세스

이제 사용자를 대신하여 Google API에 액세스하는 데 사용자 인증 정보가 필요한 작업 메서드를 컨트롤러에 추가할 수 있습니다. 다음 스니펫은 인증된 사용자의 Google 드라이브 계정에 있는 파일을 나열하는 방법을 보여줍니다. 주로 다음 두 가지 사항을 알 수 있습니다.

  • 사용자는 인증을 받아야 할 뿐만 아니라 GoogleScopedAuthorize 속성을 통해 지정하는 https://www.googleapis.com/auth/drive.readonly 범위를 애플리케이션에 부여해야 합니다.
  • Google에서는 ASP.NET Core 3의 표준 종속 항목 삽입 메커니즘을 사용하여 사용자 인증 정보를 가져오는 데 사용하는 IGoogleAuthProvider를 수신합니다.

코드:

  • 먼저 다음 using 지시어를 컨트롤러에 추가합니다.
    using Google.Apis.Auth.AspNetCore3;
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Drive.v3;
    using Google.Apis.Services;
          
  • 다음과 같이 컨트롤러 작업을 추가하고 IList<string> 모델을 수신하는 간단한 뷰와 함께 사용합니다.
    /// <summary>
    /// Lists the authenticated user's Google Drive files.
    /// Specifying the <see cref="GoogleScopedAuthorizeAttribute"> will guarantee that the code
    /// executes only if the user is authenticated and has granted the scope specified in the attribute
    /// to this application.
    /// </summary>
    /// <param name="auth">The Google authorization provider.
    /// This can also be injected on the controller constructor.</param>
    [GoogleScopedAuthorize(DriveService.ScopeConstants.DriveReadonly)]
    public async Task<IActionResult> DriveFileList([FromServices] IGoogleAuthProvider auth)
    {
        GoogleCredential cred = await auth.GetCredentialAsync();
        var service = new DriveService(new BaseClientService.Initializer
        {
            HttpClientInitializer = cred
        });
        var files = await service.Files.List().ExecuteAsync();
        var fileNames = files.Files.Select(x => x.Name).ToList();
        return View(fileNames);
    }
          

이것이 기본사항입니다. Google.Apis.Auth.AspNetCore3.IntegrationTests 프로젝트에서 HomeController.cs를 보면 다음 작업을 달성하는 방법을 알 수 있습니다.

  • 특정 범위가 없는 사용자 인증만
  • 로그아웃 기능
  • 코드를 통한 증분 승인 위의 스니펫은 속성을 통한 점진적 승인을 보여줍니다.
  • 현재 부여된 범위 검토
  • 액세스 및 갱신 토큰 검사
  • 액세스 토큰을 강제로 새로고침합니다. Google.Apis.Auth.AspNetCore3이 액세스 토큰이 만료되었거나 곧 만료되는지 감지하여 자동으로 새로고침하므로 사용자가 직접 이 작업을 수행할 필요는 없습니다.

서비스 계정

Google API는 서비스 계정도 지원합니다. 클라이언트 애플리케이션이 최종 사용자 데이터에 대한 액세스를 요청하는 시나리오와 달리 서비스 계정은 클라이언트 애플리케이션의 자체 데이터에 대한 액세스를 제공합니다.

클라이언트 애플리케이션은 Google API 콘솔에서 다운로드한 비공개 키를 사용하여 액세스 토큰 요청에 서명합니다. 새 클라이언트 ID를 만든 후 '서비스 계정' 애플리케이션 유형을 선택한 다음 비공개 키를 다운로드할 수 있습니다. Google Plus API를 사용하는 서비스 계정 샘플을 살펴보세요.

using System;
using System.Security.Cryptography.X509Certificates;

using Google.Apis.Auth.OAuth2;
using Google.Apis.Plus.v1;
using Google.Apis.Plus.v1.Data;
using Google.Apis.Services;

namespace Google.Apis.Samples.PlusServiceAccount
{
    /// <summary>
    /// This sample demonstrates the simplest use case for a Service Account service.
    /// The certificate needs to be downloaded from the Google API Console
    /// <see cref="https://console.cloud.google.com/">
    ///   "Create another client ID..." -> "Service Account" -> Download the certificate,
    ///   rename it as "key.p12" and add it to the project. Don't forget to change the Build action
    ///   to "Content" and the Copy to Output Directory to "Copy if newer".
    /// </summary>
    public class Program
    {
        // A known public activity.
        private static String ACTIVITY_ID = "z12gtjhq3qn2xxl2o224exwiqruvtda0i";

        public static void Main(string[] args)
        {
            Console.WriteLine("Plus API - Service Account");
            Console.WriteLine("==========================");

            String serviceAccountEmail = "SERVICE_ACCOUNT_EMAIL_HERE";

            var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.Exportable);

            ServiceAccountCredential credential = new ServiceAccountCredential(
               new ServiceAccountCredential.Initializer(serviceAccountEmail)
               {
                   Scopes = new[] { PlusService.Scope.PlusMe }
               }.FromCertificate(certificate));

            // Create the service.
            var service = new PlusService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "Plus API Sample",
            });

            Activity activity = service.Activities.Get(ACTIVITY_ID).Execute();
            Console.WriteLine("  Activity: " + activity.Object.Content);
            Console.WriteLine("  Video: " + activity.Object.Attachments[0].Url);

            Console.WriteLine("Press any key to continue...");
            Console.ReadKey();
        }
    }
}

위의 샘플 코드는 ServiceAccountCredential를 만듭니다. 필수 범위가 설정되고 주어진 X509Certificate2에서 비공개 키를 로드하는 FromCertificate 호출이 발생합니다. 다른 모든 샘플 코드와 마찬가지로 사용자 인증 정보는 HttpClientInitializer로 설정됩니다.