OAuth 2.0

המסמך הזה מתאר את OAuth 2.0, מתי להשתמש בו, איך לקבל מזהי לקוח ואיך להשתמש בו עם ספריית הלקוח של Google API עבור .NET.

פרוטוקול OAuth 2.0

OAuth 2.0 הוא פרוטוקול ההרשאות שמשמש את Google APIs. כדאי שתכירו את הפרוטוקול בקישורים הבאים:

משיגים סודות ומזהי לקוח

אפשר למצוא מזהי לקוח וסודות במסוף Google API. יש סוגים שונים של מזהי לקוח, לכן חשוב לבחור את הסוג המתאים לאפליקציה:

בכל אחד מקטעי הקוד שבהמשך (מלבד אחד מחשבון השירות), עליכם להוריד את סוד הלקוח ולאחסן אותו בתור client_secrets.json בפרויקט.

פרטי כניסה

פרטי הכניסה של המשתמש

'UserCredential' היא מחלקת עזרה בטוחה של שרשור לשימוש באסימון גישה כדי לגשת למשאבים מוגנים. התוקף של אסימון גישה בדרך כלל פג לאחר שעה אחת. לאחר מכן תופיע הודעת שגיאה אם תנסו להשתמש בו.

UserCredential ו-AuthorizationCodeFlow מבצעים "רענון" אוטומטי של האסימון, כלומר, קבלת אסימון גישה חדש. הפעולה הזו מתבצעת באמצעות אסימון רענון לטווח ארוך, שאותו מקבלים יחד עם אסימון הגישה אם משתמשים בפרמטר access_type=offline במהלך תהליך קוד ההרשאה.

ברוב האפליקציות מומלץ לאחסן את אסימון הגישה של פרטי הכניסה ולרענן את אסימון הרענון באחסון קבוע. אחרת, צריך להציג למשתמש הקצה דף הרשאה בדפדפן בכל שעה, כי התוקף של אסימון הגישה יפוג שעה לאחר קבלתו.

כדי לוודא שאסימוני הגישה והרענון יישמרו, ניתן לספק הטמעה משלך של IDataStore או להשתמש באחת מההטמעות הבאות שסופקו על ידי הספרייה:

  • FileDataStore ל-NET .מבטיח שפרטי הכניסה בקובץ יישארו קבועים.

ServiceAccountCredential

ServiceAccountCredential דומה ל-UserCredential, אבל הוא משמש למטרה אחרת. פרוטוקול Google OAuth 2.0 תומך באינטראקציות בין שרתים, כמו האינטראקציות בין אפליקציית אינטרנט לבין Google Cloud Storage. האפליקציה ששלחה את הבקשה צריכה להוכיח את הזהות שלה כדי לקבל גישה ל-API, ומשתמש הקצה לא חייב להיות מעורב בעניין. ב-ServiceAccountCredential נשמר מפתח פרטי שמשמש לחתימה על בקשה לקבלת אסימון גישה חדש.

גם UserCredential וגם ServiceAccountCredential מטמיעים את IConfigurableHttpClientInitializer כך שאפשר לרשום כל אחד מאלה:

  • handler של תגובה שנכשלה, לכן יתבצע רענון של האסימון אם הוא יקבל קוד מצב 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();
            ...
        }
    }
}
  
  • בקוד לדוגמה הזה, נוצר מופע חדש של UserCredential על ידי קריאה ל-method GoogleWebAuthorizationBroker.AuthorizeAsync. השיטה הסטטית הזו מקבלת את הפריטים הבאים:

    • סוד הלקוח (או העברה אל סוד הלקוח).
    • ההיקפים הנדרשים.
    • מזהה המשתמש
    • אסימון הביטול של פעולה.
    • מאגר נתונים אופציונלי. אם מאגר הנתונים לא צוין, ברירת המחדל היא FileDataStore עם תיקיית ברירת המחדל Google.Apis.Auth. התיקייה תיווצר ב-Environment.SpecialFolder.ApplicationData.
  • הערך UserCredential שמוחזר בשיטה הזו מוגדר כ-HttpClientInitializer ב-BooksService (באמצעות המאתחל). כפי שהוסבר למעלה, UserCredential מטמיעה מאתחל של לקוח HTTP.

  • שימו לב שבקוד לדוגמה שלמעלה, הפרטים של סוד הלקוח נטענים מקובץ, אבל אפשר גם לבצע את הפעולות הבאות:

    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 היא הספרייה המומלצת לרוב התרחישים של OAuth 2.0 המבוססים על Google באפליקציות ASP.NET Core 3. מוטמע בו handler של אימות OpenIdConnect, שהוא ספציפי ל-Google. היא תומכת באימות מצטבר ומגדירה IGoogleAuthProvider בהזרקה, כדי לספק פרטי כניסה ל-Google שאפשר להשתמש בהם עם Google APIs.

בקטע הזה מוסבר איך להגדיר את Google.Apis.Auth.AspNetCore3 ולהשתמש בו. הקוד שמוצג כאן מבוסס על Google.Apis.Auth.AspNetCore3.IntegrationTests – אפליקציית ASP.NET רגילה ותקינה לגמרי.

אם ברצונך לפעול לפי התיעוד הזה כמדריך, יש צורך באפליקציית 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 או חלופה דומה שבה ייתכן שאתה משתמש. קטעי הקוד הבאים נשלפו מ- Startup.cs בפרויקט Google.Apis.Auth.AspNetCore3.Integrationtests.

  • מוסיפים את ההוראה באמצעות הפקודה הבאה לקובץ Startup.cs.
    using Google.Apis.Auth.AspNetCore3;
  • בשיטה Startup.ConfigureServices, מוסיפים את הקוד הבא, משנים את ה-Client-ID ואת ה-placeholders של סוד לקוח בערכים שנכללים בקובץ client_secrets.json. אפשר לטעון את הערכים האלה ישירות מקובץ ה-JSON או לאחסן אותם בכל אופן מאובטח אחר. כדי לראות דוגמה לאופן שבו טוענים את הערכים האלה ישירות מקובץ JSON, מומלץ לעיין בשיטת ClientInfo.Load בפרויקט Google.Apis.Auth.AspNetCore3.Integrationtests.
    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 APIs בשמם

עכשיו אפשר להוסיף לאמצעי הבקרה שיטות פעולה שמחייבות את פרטי הכניסה של המשתמש כדי לגשת ל-Google APIs בשמם. קטע הקוד הבא מראה איך להציג רשימה של הקבצים בחשבון Google Drive של המשתמש המאומת. שימו לב לשני דברים בעיקר:

  • בנוסף לביצוע האימות של המשתמש, הוא צריך גם לתת לאפליקציה את ההיקף https://www.googleapis.com/auth/drive.readonly, שאותו מציינים באמצעות המאפיין GoogleScopedAuthorize.
  • אנחנו משתמשים במנגנון הזרקת תלות סטנדרטי של 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);
    }
          

ואלה העקרונות הבסיסיים. כדי לגלות איך להשיג את היעדים הבאים, כדאי לעיין בקובץ HomeController.cs מהפרויקט Google.Apis.Auth.AspNetCore3.Integrationtests

  • אימות משתמשים בלבד, ללא היקפים ספציפיים
  • פונקציית התנתקות
  • הרשאה מצטברת באמצעות קוד. שימו לב שבקטע הקוד שלמעלה מוצגת הרשאה מצטברת באמצעות מאפיינים.
  • בדיקה של ההיקפים שהוענקו כרגע
  • בדיקת אסימוני הגישה ורענון
  • רענון ידני של אסימון הגישה. לתשומת ליבך, אין צורך לעשות זאת בעצמך כי Google.Apis.Auth.AspNetCore3 יזהה אם פג תוקפו של אסימון הגישה או שהוא עומד לפוג, ותרענן אותו באופן אוטומטי.

חשבון שירות

ממשקי Google API תומכים גם בחשבונות שירות. בניגוד לתרחיש שבו אפליקציית לקוח מבקשת גישה לנתונים של משתמש קצה, חשבונות שירות מספקים גישה לנתונים של אפליקציית הלקוח עצמה.

אפליקציית הלקוח שלך חותמת על הבקשה לקבלת אסימון גישה באמצעות מפתח פרטי שהורד ממסוף Google API. אחרי שיוצרים מזהה לקוח חדש, בוחרים את סוג האפליקציה 'חשבון שירות', ואז אפשר להוריד את המפתח הפרטי. אפשר לעיין בדוגמה של חשבון השירות באמצעות 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. ההיקפים הנדרשים הוגדרו ומתבצעת קריאה ל-FromCertificate, שטוענת את המפתח הפרטי מה-X509Certificate2 הנתון. כמו בכל שאר הקוד לדוגמה, פרטי הכניסה מוגדרים כ-HttpClientInitializer.