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 של תגובה נכשל, לכן הוא ירענן את האסימון אם הוא יקבל קוד סטטוס 401 של HTTP.
  • מיירט, ליירט את 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 חדשה על ידי קריאה 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 APIs OAuth 2.0 לאפליקציות של שרת אינטרנט

Google.Apis.Auth.AspNetCore3 היא הספרייה המומלצת לשימוש ברוב האפליקציות מבוססות Google תרחישי OAuth 2.0 באפליקציות ASP.NET Core 3. הוא מטמיעים מודל שפה ספציפי handler של האימות OpenIdConnect. הוא תומך באימות מצטבר ומגדיר פעולה ניתנת להזרקה IGoogleAuthProvider כדי לספק פרטי כניסה ל-Google שבהם אפשר להשתמש עם Google APIs.

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

אם אתם רוצים לעיין בתיעוד הזה כמדריך, תצטרכו להשתמש ב-ASP.NET משלכם. הגשת מועמדות לקורס ליבה 3, ולהשלים את השלבים האלה כדרישה מוקדמת.

דרישות מוקדמות

  • להתקין את חבילת Google.Apis.Auth.AspNetCore3.
  • אנחנו משתמשים ב-Google Drive API צריך להתקין גם את Google.Apis.Drive.v3 חבילה.
  • אם עדיין אין לך פרויקט, עליך ליצור פרויקט ב-Google Cloud. הוספה למעקב לבצע את ההוראות האלה. זה יהיה הפרויקט שאיתו האפליקציה תזוהה.
  • חשוב לוודא שהפעלת את Google Drive API. כדי להפעיל ממשקי API: הוראות אלה.
  • יצירת פרטי כניסה להרשאה שיאפשרו ל-Google לזהות את האפליקציה שלך. הוספה למעקב את ההוראות האלה כדי ליצור פרטי כניסה להרשאות ולהוריד את קובץ client_secrets.json. שני דברים חשובים:
    • שימו לב שפרטי הכניסה הסוג חייב להיות Web application.
    • כדי להפעיל את האפליקציה הזו, ה-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, מוסיפים את הקוד הבא, משנים את placeholders של Client-ID ו-Client Secret עם הערכים הכלולים ברכיב ה-placeholder קובץ client_secrets.json. אפשר לטעון את הערכים האלה ישירות מקובץ ה-JSON או שניתן לאחסן אותם בכל אופן מאובטח אחר. כדאי לעיין ClientInfo.Load ב-Google.Apis.Auth.AspNetCore3.IntegrationTests דוגמה לאופן שבו אפשר לטעון את הערכים האלה ישירות מקובץ ה-JSON.
    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};
            });
    }
          
  • ב-method 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 Console. אחרי שיוצרים מזהה לקוח חדש, צריך לבחור 'חשבון שירות' סוג האפליקציה, ואז אפשר להוריד את המפתח הפרטי. כדאי לעיין דוגמה של חשבון שירות באמצעות 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.