OAuth 2.0

이 문서에서는 OAuth 2.0, OAuth 2.0 사용 시기, 클라이언트 ID를 가져오는 방법, .NET용 Google API 클라이언트 라이브러리와 함께 사용하는 방법을 설명합니다.

OAuth 2.0 프로토콜

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

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

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

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

사용자 인증 정보

사용자 인증 정보

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

UserCredentialAuthorizationCodeFlow는 토큰을 자동으로 '갱신'합니다. 즉, 새 액세스 토큰을 가져옵니다. 승인 코드 흐름 중에 access_type=offline 매개변수를 사용하면 액세스 토큰과 함께 제공되는 장기 갱신 토큰이 사용됩니다.

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

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

  • .NET용 FileDataStore은 사용자 인증 정보가 파일에 영구적으로 있는지 확인합니다.

ServiceAccountCredential

ServiceAccountCredentialUserCredential와 유사하지만 목적이 다릅니다. Google OAuth 2.0은 웹 애플리케이션과 Google Cloud Storage 간의 상호작용과 같은 서버 간 상호작용을 지원합니다. 요청 애플리케이션은 자체 ID를 입증하여 API에 액세스해야 하며 최종 사용자는 개입할 필요가 없습니다. 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 파일을 다운로드하세요. 2가지 주요 사항은 다음과 같습니다.
    • 사용자 인증 정보 유형은 웹 애플리케이션이어야 합니다.
    • 이 앱을 실행하는 경우 추가해야 하는 리디렉션 URI는 https://localhost:5001/signin-oidc뿐입니다.

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

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

  • 다음 사용 지시문을 Startup.cs 파일에 추가합니다.
    using Google.Apis.Auth.AspNetCore3;
  • Startup.ConfigureServices 메서드에서 다음 코드를 추가하고 client_secrets.json 파일에 포함된 값을 사용하여 클라이언트 ID와 클라이언트 보안 비밀번호 자리표시자를 변경합니다. 이러한 값은 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 메서드에서 HTTPS 리디렉션과 ASP.NET Core 3 인증 및 승인 미들웨어 구성요소를 파이프라인에 추가해야 합니다.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...
        app.UseHttpsRedirection();
        ...
    
        app.UseAuthentication();
        app.UseAuthorization();
    
        ...
    }
          

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

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

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

코드는 다음과 같습니다.

  • 먼저 컨트롤러에 다음 사용 지시어를 추가합니다.
    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이 액세스 토큰이 만료되었거나 만료 임박한 것을 감지하고 자동으로 새로고침하므로 이 작업을 직접 수행할 필요가 없습니다.

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

Google API는 웹 서버 애플리케이션용 OAuth 2.0을 지원합니다. 다음 코드를 성공적으로 실행하려면 먼저 Google API 콘솔에서 프로젝트에 리디렉션 URI를 추가해야 합니다. FlowMetadata 및 기본 설정을 사용하므로 리디렉션 URI를 your_site/AuthCallback/IndexAsync로 설정합니다.

OAuth 2.0 사용자 인증 정보의 리디렉션 URI를 찾으려면 다음 안내를 따르세요.

  1. API 콘솔에서 사용자 인증 정보 페이지를 엽니다.
  2. 아직 OAuth 2.0 사용자 인증 정보를 만들지 않았다면 사용자 인증 정보 만들기 > OAuth 클라이언트 ID를 클릭합니다.
  3. 사용자 인증 정보를 만든 후 OAuth 2.0 클라이언트 ID 섹션에서 클라이언트 ID (웹 애플리케이션용)를 클릭하여 리디렉션 URL을 보거나 수정합니다.

IDE에서 새 웹 애플리케이션 프로젝트를 만든 후 Drive, YouTube 또는 사용하려는 다른 서비스에 적합한 Google.Apis NuGet 패키지를 추가합니다. 그런 다음 Google.Apis.Auth.MVC 패키지를 추가합니다. 다음 코드는 Google API 서비스를 쿼리하는 ASP.NET MVC 애플리케이션을 보여줍니다.

  1. FlowMetadata의 자체 구현을 추가합니다.
    using System;
    using System.Web.Mvc;
    
    using Google.Apis.Auth.OAuth2;
    using Google.Apis.Auth.OAuth2.Flows;
    using Google.Apis.Auth.OAuth2.Mvc;
    using Google.Apis.Drive.v2;
    using Google.Apis.Util.Store;
    
    namespace Google.Apis.Sample.MVC4
    {
        public class AppFlowMetadata : FlowMetadata
        {
            private static readonly IAuthorizationCodeFlow flow =
                new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
                    {
                        ClientSecrets = new ClientSecrets
                        {
                            ClientId = "PUT_CLIENT_ID_HERE",
                            ClientSecret = "PUT_CLIENT_SECRET_HERE"
                        },
                        Scopes = new[] { DriveService.Scope.Drive },
                        DataStore = new FileDataStore("Drive.Api.Auth.Store")
                    });
    
            public override string GetUserId(Controller controller)
            {
                // In this sample we use the session to store the user identifiers.
                // That's not the best practice, because you should have a logic to identify
                // a user. You might want to use "OpenID Connect".
                // You can read more about the protocol in the following link:
                // https://developers.google.com/accounts/docs/OAuth2Login.
                var user = controller.Session["user"];
                if (user == null)
                {
                    user = Guid.NewGuid();
                    controller.Session["user"] = user;
                }
                return user.ToString();
    
            }
    
            public override IAuthorizationCodeFlow Flow
            {
                get { return flow; }
            }
        }
    }
          

    FlowMetadata는 사용자 식별자와 사용 중인 IAuthorizationCodeFlow를 가져오는 고유한 로직을 포함하는 추상 클래스입니다.

    위의 샘플 코드에서는 올바른 범위, 클라이언트 보안 비밀번호, 데이터 저장소를 사용하여 새 GoogleAuthorizationCodeFlow가 생성됩니다. IDataStore의 자체 구현을 추가해 보세요. 예를 들어 EntityFramework를 사용하는 구현을 작성할 수 있습니다.

  2. Google API 서비스를 사용하는 자체 컨트롤러를 구현합니다. 다음 샘플에서는 DriveService를 사용합니다.
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Web.Mvc;
    
    using Google.Apis.Auth.OAuth2.Mvc;
    using Google.Apis.Drive.v2;
    using Google.Apis.Services;
    
    using Google.Apis.Sample.MVC4;
    
    namespace Google.Apis.Sample.MVC4.Controllers
    {
        public class HomeController : Controller
        {
            public async Task<ActionResult> IndexAsync(CancellationToken cancellationToken)
            {
                var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).
                    AuthorizeAsync(cancellationToken);
    
                if (result.Credential != null)
                {
                    var service = new DriveService(new BaseClientService.Initializer
                        {
                            HttpClientInitializer = result.Credential,
                            ApplicationName = "ASP.NET MVC Sample"
                        });
    
                    // YOUR CODE SHOULD BE HERE..
                    // SAMPLE CODE:
                    var list = await service.Files.List().ExecuteAsync();
                    ViewBag.Message = "FILE COUNT IS: " + list.Items.Count();
                    return View();
                }
                else
                {
                    return new RedirectResult(result.RedirectUri);
                }
            }
        }
    }
          
  3. 자체 콜백 컨트롤러를 구현합니다. 구현은 다음과 같이 표시됩니다.
    using Google.Apis.Sample.MVC4;
    
    namespace Google.Apis.Sample.MVC4.Controllers
    {
        public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController
        {
            protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData
            {
                get { return new AppFlowMetadata(); }
            }
        }
    }
          

서비스 계정

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로 설정됩니다.