OAuth 2.0

يوضّح هذا المستند بروتوكول OAuth 2.0، وحالات استخدامه، وكيفية الحصول على معرّفات العملاء، وكيفية استخدامه مع مكتبة برامج Google API لنظام ‎ .NET.

بروتوكول OAuth 2.0

‫OAuth 2.0 هو بروتوكول التفويض الذي تستخدمه Google APIs. يجب الاطّلاع على البروتوكول من خلال الروابط التالية:

الحصول على معرّفات العملاء وأسرارهم

يمكنك الحصول على معرّفات العملاء والأسرار في وحدة تحكّم واجهة برمجة تطبيقات Google. هناك أنواع مختلفة من أرقام تعريف العملاء، لذا احرص على الحصول على النوع الصحيح لتطبيقك:

في كل مقتطف من مقتطفات الرموز البرمجية المعروضة (باستثناء مقتطف حساب الخدمة)، عليك تنزيل مفتاح سر العميل وتخزينه باسم client_secrets.json في مشروعك.

بيانات الاعتماد

بيانات اعتماد المستخدم

UserCredential هي فئة مساعدة آمنة لسلسلة المهام لاستخدام رمز مميّز للوصول إلى الموارد المحمية. تنتهي صلاحية الرمز المميّز للوصول عادةً بعد ساعة واحدة، وبعد ذلك ستظهر لك رسالة خطأ إذا حاولت استخدامه.

تحرص كل من UserCredential و AuthorizationCodeFlow على "إعادة تحميل" الرمز المميّز تلقائيًا، ما يعني الحصول على رمز دخول جديد. ويتم ذلك باستخدام رمز مميّز طويل الأمد لإعادة التحميل، والذي تتلقّاه مع رمز الوصول في حال استخدام المَعلمة access_type=offline أثناء مسار رمز التفويض.

في معظم التطبيقات، من المستحسن تخزين رمزَي هُوية الدخول وإعادة التنشيط لبيانات الاعتماد في مساحة تخزين دائمة. بخلاف ذلك، عليك عرض صفحة تفويض في المتصفّح للمستخدم النهائي كل ساعة، لأنّه تنتهي صلاحية رمز عبور الوصول بعد ساعة من استلامه.

لضمان استمرار رمزَي الوصول وإعادة التنشيط، يمكنك تقديم طريقة تنفيذ خاصة بك لرمز IDataStore، أو يمكنك استخدام إحدى طرق التنفيذ التالية التي تقدّمها المكتبة:

  • يضمن FileDataStore لـ .NET أن تكون بيانات الاعتماد دائمة في ملف.

ServiceAccountCredential

ServiceAccountCredential يشبه UserCredential، ولكنّه يخدم غرضًا مختلفًا. يتيح Google OAuth 2.0 التفاعلات بين الخوادم، مثل التفاعلات بين تطبيق ويب وGoogle Cloud Storage. على التطبيق المُرسِل إثبات هويته للوصول إلى واجهة برمجة التطبيقات، ولا يلزم أن يكون هناك مستخدم نهائي. تخزِّن ServiceAccountCredential مفتاحًا خاصًا يُستخدَم لتوقيع طلب للحصول على رمز دخول جديد.

ينفِّذ كلّ من UserCredential وServiceAccountCredential 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();
            ...
        }
    }
}
  
  • في نموذج الرمز البرمجي هذا، يتم إنشاء مثيل جديد من 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 هي المكتبة المُقترَحة لاستخدامها في معظم سيناريوهات OAuth 2.0 المستندة إلى Google في تطبيقات ASP.NET Core 3. وتنفِّذ معالج مصادقة OpenIdConnect خاصًا بـ Google. وهو يتيح المصادقة المتزايدة، ويحدّد IGoogleAuthProvider قابلاً للحقن لتوفير بيانات اعتماد Google التي يمكن استخدامها مع Google APIs.

يصف هذا القسم كيفية ضبط Google.Apis.Auth.AspNetCore3 واستخدامه. يستند الرمز المعروض هنا إلى Google.Apis.Auth.AspNetCore3.IntegrationTests وهو تطبيق ASP.NET Core 3 عادي يعمل بشكل كامل.

إذا كنت تريد اتّباع هذه المستندات كمرجع تعليمي، ستحتاج إلى تطبيق ASP.NET Core 3 الخاص بك وإكمال هذه الخطوات كشرط أساسي.

المتطلبات الأساسية

  • ثبِّت الحزمة Google.Apis.Auth.AspNetCore3.
  • نحن نستخدم Google Drive API، لذا عليك أيضًا تثبيت حزمة Google.Apis.Drive.v3.
  • أنشئ مشروعًا على Google Cloud إذا لم يكن لديك مشروع. اتّبِع هذه التعليمات لإجراء ذلك. وسيكون هذا هو المشروع الذي يتم التعرّف على تطبيقك من خلاله.
  • احرص على تفعيل Google Drive API. لتفعيل واجهات برمجة التطبيقات، يُرجى اتّباع هذه التعليمات.
  • أنشئ بيانات اعتماد تفويض تحدِّد هوية تطبيقك لـ Google. اتّبِع هذه التعليمات لإنشاء بيانات اعتماد التفويض وتنزيلملف client_secrets.json. هناك نقطتان مهمتان:
    • يُرجى العلم أنّ نوع بيانات الاعتماد يجب أن يكون تطبيق ويب.
    • لتشغيل هذا التطبيق، عنوان URL الوحيد لإعادة التوجيه الذي يجب إضافته هو 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_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};
            });
    }
          
  • في طريقة 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 APIs أيضًا استخدام حسابات الخدمة. على عكس السيناريو الذي يطلب فيه تطبيق العميل الوصول إلى بيانات المستخدم النهائي، توفّر حسابات الخدمة إمكانية الوصول إلى بيانات تطبيق العميل.

يوقّع تطبيق العميل طلب الحصول على رمز مميّز للوصول باستخدام مفتاح خاص تم تنزيله من وحدة تحكّم واجهة برمجة تطبيقات Google. بعد إنشاء معرّف عميل جديد، عليك اختيار نوع تطبيق حساب الخدمة، ثم يمكنك تنزيل المفتاح الخاص. اطّلِع على مثال حساب الخدمة باستخدام 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.