تسجيل الدخول بحساب Google للتطبيقات من جهة الخادم

لاستخدام خدمات Google نيابةً عن مستخدم عندما يكون غير متصل بالإنترنت، يجب استخدام عملية مختلطة من جهة الخادم حيث يمنح المستخدم إذنًا لتطبيقك من جهة العميل باستخدام JavaScript API client، وتُرسِل رمزًا خاصًا لمنح الإذن لمرة واحدة إلى خادمك. يتبادل الخادم هذا الرمز المخصّص للاستخدام مرة واحدة للحصول على الرموز المميّزة الخاصة بالوصول وإعادة التنشيط من Google لكي يتمكّن من تنفيذ طلبات البيانات من واجهة برمجة التطبيقات، ويمكن إجراء ذلك عندما يكون المستخدم غير متصل بالإنترنت. توفّر عملية إنشاء الرمز المُستخدَم لمرة واحدة مزايا أمان على كلّ من عملية الإنشاء من جهة الخادم فقط وإرسال الرموز المميّزة للوصول إلى خادمك.

في ما يلي توضيح لخطوات تسجيل الدخول للحصول على رمز مميّز للوصول إلى التطبيق من جهة الخادم.

تتمتع الرموز المخصّصة للاستخدام مرة واحدة بعدة مزايا أمنية. باستخدام الرموز، تقدّم Google الرموز المميّزة مباشرةً إلى خادمك بدون أي وسطاء. على الرغم من أنّنا لا ننصح بإفشاء الرموز، إلا أنّه من الصعب جدًا استخدامها بدون مفتاح سر العميل. يجب الحفاظ على سرية سر العميل.

تنفيذ مسار الرمز المُستخدَم لمرة واحدة

يقدّم زر "تسجيل الدخول باستخدام حساب Google" كلاً من رمز دخول ورمز تفويض. الرمز هو رمز صالح لمرة واحدة يمكن لخادمك تبادله مع خوادم Google للحصول على رمز مميّز للوصول.

يوضّح الرمز البرمجي النموذجي التالي كيفية تنفيذ عملية الرمز المُستخدَم لمرة واحدة.

تتطلّب مصادقة تسجيل الدخول باستخدام حساب Google من خلال مسار الرمز المُستخدَم لمرة واحدة ما يلي:

الخطوة 1: إنشاء معرِّف عميل وسر عميل

لإنشاء معرّف عميل وسرّ عميل، أنشئ مشروعًا على Google API Console، وأعِد إعداد معرّف عميل OAuth، وسجِّل مصادر JavaScript:

  1. انتقِل إلى وحدة التحكم في واجهة Google API.

  2. من القائمة المنسدلة للمشروع، اختَر مشروعًا حاليًا أو أنشِئ مشروعًا جديدًا من خلال اختيار إنشاء مشروع جديد.

  3. في الشريط الجانبي ضمن "واجهات برمجة التطبيقات والخدمات"، اختَر بيانات الاعتماد، ثم انقر على ضبط شاشة الموافقة.

    اختَر عنوان بريد إلكتروني وحدِّد اسم منتج، ثم اضغط على حفظ.

  4. في علامة التبويب بيانات الاعتماد، اختَر القائمة المنسدلة إنشاء بيانات اعتماد، ثم اختَر معرِّف عميل OAuth.

  5. ضمن نوع التطبيق، اختَر تطبيق الويب.

    سجِّل مصادر الإذن التي يحصل عليها تطبيقك للوصول إلى Google APIs، على النحو التالي. المصدر هو مزيج فريد من البروتوكول واسم المضيف والمنفذ.

    1. في الحقل مصادر JavaScript المعتمَدة، أدخِل مصدر تطبيقك. يمكنك إدخال مصادر متعددة للسماح بتشغيل تطبيقك على بروتوكولات أو نطاقات أو نطاقات فرعية مختلفة. لا يمكنك استخدام أحرف البدل. في المثال أدناه، يمكن أن يكون عنوان URL الثاني هو عنوان URL للإصدار العلني.

      http://localhost:8080
      https://myproductionurl.example.com
      
    2. لا يتطلّب حقل معرّف الموارد المنتظم (URI) المفوَّض لإعادة التوجيه إدخال قيمة. لا يتم استخدام عناوين URL الخاصة بإعادة التوجيه مع واجهات برمجة تطبيقات JavaScript.

    3. اضغط على الزر إنشاء.

  6. من مربّع حوار عميل OAuth الناتج، انسخ معرِّف العميل. يتيح معرّف العميل لتطبيقك الوصول إلى واجهات برمجة تطبيقات Google المفعّلة.

الخطوة 2: تضمين مكتبة منصة Google في صفحتك

أدرِج النصوص البرمجية التالية التي توضّح دالة مجهولة الهوية تُدخِل نصًا برمجيًا في نموذج DOM لصفحة الويب index.html هذه.

<!-- The top of file index.html -->
<html itemscope itemtype="http://schema.org/Article">
<head>
  <!-- BEGIN Pre-requisites -->
  <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js">
  </script>
  <script src="https://apis.google.com/js/client:platform.js?onload=start" async defer>
  </script>
  <!-- END Pre-requisites -->

الخطوة 3: بدء عنصر GoogleAuth

حمِّل مكتبة auth2 واتصل بـ gapi.auth2.init() لبدء معالجة GoogleAuth. حدِّد معرِّف العميل والنطاقات التي تريد طلبها عند الاتصال بالرقم init().

<!-- Continuing the <head> section -->
  <script>
    function start() {
      gapi.load('auth2', function() {
        auth2 = gapi.auth2.init({
          client_id: 'YOUR_CLIENT_ID.apps.googleusercontent.com',
          // Scopes to request in addition to 'profile' and 'email'
          //scope: 'additional_scope'
        });
      });
    }
  </script>
</head>
<body>
  <!-- ... -->
</body>
</html>

الخطوة 4: إضافة زر تسجيل الدخول إلى صفحتك

أضِف زر تسجيل الدخول إلى صفحة الويب، وأضِف معالِج نقرة للاتّصال بـ grantOfflineAccess() لبدء عملية استخدام الرمز المُستخدَم لمرة واحدة.

<!-- Add where you want your sign-in button to render -->
<!-- Use an image that follows the branding guidelines in a real app -->
<button id="signinButton">Sign in with Google</button>
<script>
  $('#signinButton').click(function() {
    // signInCallback defined in step 6.
    auth2.grantOfflineAccess().then(signInCallback);
  });
</script>

الخطوة 5: تسجيل دخول المستخدم

ينقر المستخدم على زر تسجيل الدخول ويمنح تطبيقك إذن الوصول إلى الأذونات التي طلبتها. بعد ذلك، يتمّ تمرير عنصر JSON يحتوي على رمز التفويض إلى دالّة ردّ الاتصال التي حدّدتها في الأسلوب grantOfflineAccess().then(). على سبيل المثال:

{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}

الخطوة 6: إرسال رمز التفويض إلى الخادم

code هو رمز صالح لمرة واحدة يمكن أن يستبدله الخادم برمزَي code الوصول والتحديث. لا يمكنك الحصول على رمز إعادة التنشيط إلا بعد أن يتم عرض مربّع حوار تفويض على المستخدم يطلب الوصول إلى البيانات بلا إنترنت. إذا حدّدت select-account prompt في OfflineAccessOptions في الخطوة 4، عليك تخزين الرمز المميّز لإعادة التحميل الذي تسترجعه لاستخدامه لاحقًا، لأنّ عمليات التبادل اللاحقة ستُعرِض null للرمز المميّز لإعادة التحميل. يقدّم هذا المسار مستوى أمان أعلى مقارنةً بمسار OAuth 2.0 العادي.

يتم دائمًا عرض الرموز المميّزة للوصول من خلال استبدال رمز تفويض صالح.

يحدِّد النص البرمجي التالي دالة ردّ اتصال لزرّ تسجيل الدخول. عند تسجيل دخول بنجاح، تخزِّن الدالة الرمز المميّز للوصول لاستخدامه من جانب العميل وتُرسِل الرمز المخصّص لمرة واحدة إلى خادمك في النطاق نفسه.

<!-- Last part of BODY element in file index.html -->
<script>
function signInCallback(authResult) {
  if (authResult['code']) {

    // Hide the sign-in button now that the user is authorized, for example:
    $('#signinButton').attr('style', 'display: none');

    // Send the code to the server
    $.ajax({
      type: 'POST',
      url: 'http://example.com/storeauthcode',
      // Always include an `X-Requested-With` header in every AJAX request,
      // to protect against CSRF attacks.
      headers: {
        'X-Requested-With': 'XMLHttpRequest'
      },
      contentType: 'application/octet-stream; charset=utf-8',
      success: function(result) {
        // Handle or verify the server response.
      },
      processData: false,
      data: authResult['code']
    });
  } else {
    // There was an error.
  }
}
</script>

الخطوة 7: استبدال رمز التفويض برمز دخول

على الخادم، استبدِل رمز التفويض برموز الدخول وإعادة التحميل. استخدِم رمز الوصول لاستدعاء Google APIs نيابةً عن المستخدم، ويمكنك اختياريًا تخزين رمز إعادة التحميل للحصول على رمز وصول جديد عند انتهاء صلاحية رمز الوصول.

إذا طلبت الوصول إلى الملف الشخصي، ستحصل أيضًا على رمز تعريف يحتوي على معلومات أساسية عن الملف الشخصي للمستخدم.

على سبيل المثال:

Java
// (Receive authCode via HTTPS POST)


if (request.getHeader("X-Requested-With") == null) {
  // Without the `X-Requested-With` header, this request could be forged. Aborts.
}

// Set path to the Web application client_secret_*.json file you downloaded from the
// Google API Console: https://console.cloud.google.com/apis/credentials
// You can also find your Web application client ID and client secret from the
// console and specify them directly when you create the GoogleAuthorizationCodeTokenRequest
// object.
String CLIENT_SECRET_FILE = "/path/to/client_secret.json";

// Exchange auth code for access token
GoogleClientSecrets clientSecrets =
    GoogleClientSecrets.load(
        JacksonFactory.getDefaultInstance(), new FileReader(CLIENT_SECRET_FILE));
GoogleTokenResponse tokenResponse =
          new GoogleAuthorizationCodeTokenRequest(
              new NetHttpTransport(),
              JacksonFactory.getDefaultInstance(),
              "https://oauth2.googleapis.com/token",
              clientSecrets.getDetails().getClientId(),
              clientSecrets.getDetails().getClientSecret(),
              authCode,
              REDIRECT_URI)  // Specify the same redirect URI that you use with your web
                             // app. If you don't have a web version of your app, you can
                             // specify an empty string.
              .execute();

String accessToken = tokenResponse.getAccessToken();

// Use access token to call API
GoogleCredential credential = new GoogleCredential().setAccessToken(accessToken);
Drive drive =
    new Drive.Builder(new NetHttpTransport(), JacksonFactory.getDefaultInstance(), credential)
        .setApplicationName("Auth Code Exchange Demo")
        .build();
File file = drive.files().get("appfolder").execute();

// Get profile info from ID token
GoogleIdToken idToken = tokenResponse.parseIdToken();
GoogleIdToken.Payload payload = idToken.getPayload();
String userId = payload.getSubject();  // Use this value as a key to identify a user.
String email = payload.getEmail();
boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
String name = (String) payload.get("name");
String pictureUrl = (String) payload.get("picture");
String locale = (String) payload.get("locale");
String familyName = (String) payload.get("family_name");
String givenName = (String) payload.get("given_name");
Python
from apiclient import discovery
import httplib2
from oauth2client import client

# (Receive auth_code by HTTPS POST)


# If this request does not have `X-Requested-With` header, this could be a CSRF
if not request.headers.get('X-Requested-With'):
    abort(403)

# Set path to the Web application client_secret_*.json file you downloaded from the
# Google API Console: https://console.cloud.google.com/apis/credentials
CLIENT_SECRET_FILE = '/path/to/client_secret.json'

# Exchange auth code for access token, refresh token, and ID token
credentials = client.credentials_from_clientsecrets_and_code(
    CLIENT_SECRET_FILE,
    ['https://www.googleapis.com/auth/drive.appdata', 'profile', 'email'],
    auth_code)

# Call Google API
http_auth = credentials.authorize(httplib2.Http())
drive_service = discovery.build('drive', 'v3', http=http_auth)
appfolder = drive_service.files().get(fileId='appfolder').execute()

# Get profile info from ID token
userid = credentials.id_token['sub']
email = credentials.id_token['email']