تسجيل الدخول إلى الحساب المرتبط على أجهزة Android

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

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

في هذا المستند، ستتعلم كيفية تعديل تطبيق Android لإتاحة تسجيل الدخول إلى الحساب المرتبط.

آلية العمل

  1. يمكنك تفعيل عرض الحسابات المرتبطة أثناء عملية "تسجيل الدخول بنقرة واحدة".
  2. إذا سجّل المستخدم الدخول على Google وربط حسابه على Google بحسابه على خدمتك، سيتم عرض رمز المعرّف المميز للحساب المرتبط.
  3. يظهر للمستخدم رسالة مطالبة بتسجيل الدخول بنقرة واحدة مع خيار تسجيل الدخول إلى خدمتك باستخدام حسابه المرتبط.
  4. إذا اختار المستخدم المتابعة باستخدام الحساب المرتبط، يتم إرجاع الرمز المميز لمعرّف المستخدم إلى تطبيقك. ويمكنك مطابقة ذلك مع الرمز المميز الذي تم إرساله إلى خادمك في الخطوة الثانية لتحديد المستخدم الذي سجّل الدخول.

ضبط إعدادات الجهاز

إعداد بيئة التطوير

احصل على أحدث "خدمات Google Play" على مضيف التطوير الذي تستخدمه:

  1. افتح مدير SDK لنظام التشغيل Android.
  1. ضمن أدوات SDK، ابحث عن خدمات Google Play.

  2. إذا لم تكن حالة هاتين الحِزمتَين مثبّتة، اختَرهما معًا وانقر على تثبيت الحزم.

ضبط إعدادات تطبيقك

  1. في ملف build.gradle على مستوى المشروع، أدرِج مستودع Maven من Google في كل من القسمَين buildscript وallprojects.

    buildscript {
        repositories {
            google()
        }
    }
    
    allprojects {
        repositories {
            google()
        }
    }
    
  2. أضِف التبعيات الخاصة بواجهة برمجة التطبيقات "Link with Google" إلى ملف Gradle على مستوى التطبيق في وحدتك، والذي يكون عادةً app/build.gradle:

    dependencies {
      implementation 'com.google.android.gms:play-services-auth:21.0.0'
    }
    

تعديل تطبيق Android لإتاحة تسجيل الدخول إلى حساب مرتبط

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

يوضّح نموذج الرمز البرمجي التالي بالتفصيل خطوات استرداد والتحقّق من الرمز المميّز للمعرّف، وبعد ذلك تسجيل دخول المستخدم.

  1. إنشاء نشاط للحصول على نتيجة الغرض من تسجيل الدخول

    Kotlin

      private val activityResultLauncher = registerForActivityResult(
        ActivityResultContracts.StartIntentSenderForResult()) { result ->
        if (result.resultCode == RESULT_OK) {
          try {
            val signInCredentials = Identity.signInClient(this)
                                    .signInCredentialFromIntent(result.data)
            // Review the Verify the integrity of the ID token section for
            // details on how to verify the ID token
            verifyIdToken(signInCredential.googleIdToken)
          } catch (e: ApiException) {
            Log.e(TAG, "Sign-in failed with error code:", e)
          }
        } else {
          Log.e(TAG, "Sign-in failed")
        }
      }
    

    Java

      private final ActivityResultLauncher<IntentSenderResult>
        activityResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartIntentSenderForResult(),
        result -> {
        If (result.getResultCode() == RESULT_OK) {
            try {
              SignInCredential signInCredential = Identity.getSignInClient(this)
                             .getSignInCredentialFromIntent(result.getData());
              verifyIdToken(signInCredential.getGoogleIdToken());
            } catch (e: ApiException ) {
              Log.e(TAG, "Sign-in failed with error:", e)
            }
        } else {
            Log.e(TAG, "Sign-in failed")
        }
    });
    
  2. إنشاء طلب تسجيل الدخول

    Kotlin

    private val tokenRequestOptions =
    GoogleIdTokenRequestOptions.Builder()
      .supported(true)
      // Your server's client ID, not your Android client ID.
      .serverClientId(getString("your-server-client-id")
      .filterByAuthorizedAccounts(true)
      .associateLinkedAccounts("service-id-of-and-defined-by-developer",
                               scopes)
      .build()
    

    Java

     private final GoogleIdTokenRequestOptions tokenRequestOptions =
         GoogleIdTokenRequestOptions.Builder()
      .setSupported(true)
      .setServerClientId("your-service-client-id")
      .setFilterByAuthorizedAccounts(true)
      .associateLinkedAccounts("service-id-of-and-defined-by-developer",
                                scopes)
      .build()
    
  3. إطلاق الغرض في انتظار تسجيل الدخول في انتظار المراجعة

    Kotlin

     Identity.signInClient(this)
        .beginSignIn(
      BeginSignInRequest.Builder()
        .googleIdTokenRequestOptions(tokenRequestOptions)
      .build())
        .addOnSuccessListener{result ->
          activityResultLauncher.launch(result.pendingIntent.intentSender)
      }
      .addOnFailureListener {e ->
        Log.e(TAG, "Sign-in failed because:", e)
      }
    

    Java

     Identity.getSignInClient(this)
      .beginSignIn(
        BeginSignInRequest.Builder()
          .setGoogleIdTokenRequestOptions(tokenRequestOptions)
          .build())
      .addOnSuccessListener(result -> {
        activityResultLauncher.launch(
            result.getPendingIntent().getIntentSender());
    })
    .addOnFailureListener(e -> {
      Log.e(TAG, "Sign-in failed because:", e);
    });
    

التحقّق من سلامة الرمز المميّز لرقم التعريف

للتحقّق من صلاحية الرمز المميّز، احرص على استيفاء المعايير التالية:

  • وقّعت Google الرمز المميّز لرقم التعريف بشكل صحيح. استخدِم مفاتيح Google العامة (المتوفّرة بتنسيق JWK أو PEM) للتحقّق من توقيع الرمز المميّز. يتم تدوير هذه المفاتيح بانتظام، لذا يمكنك فحص عنوان Cache-Control في الاستجابة لتحديد الوقت المناسب لاستردادها مرة أخرى.
  • قيمة aud في الرمز المميّز للرقم التعريفي تساوي أحد معرِّفات عملاء تطبيقك. وتُعدّ عملية التحقّق هذه ضرورية لمنع الرموز المميّزة لرقم التعريف التي يتم إصدارها لتطبيق ضار عند استخدامها للوصول إلى بيانات المستخدم نفسه على خادم الخلفية في تطبيقك.
  • قيمة iss في الرمز المميّز للمعرّف تساوي accounts.google.com أو https://accounts.google.com.
  • لم يمر وقت انتهاء صلاحية (exp) الرمز المميّز للمعرّف.
  • إذا كنت بحاجة إلى التحقق من أنّ الرمز المميّز لرقم التعريف يمثّل حساب مؤسسة على Google Workspace أو Cloud، يمكنك مراجعة مطالبة hd التي تشير إلى النطاق المستضاف للمستخدم. ويجب استخدام هذه الطريقة عند تقييد الوصول إلى مورد معيّن على أعضاء نطاقات معيّنة فقط. في حال عدم تقديم هذا الادّعاء، يشير إلى أنّ الحساب لا ينتمي إلى نطاق تستضيفه Google.

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

استخدام مكتبة برامج Google API

إن استخدام مكتبة برامج Google API لـ Java هو الطريقة الموصى بها للتحقق من صحة رموز معرّف Google في بيئة إنتاج.

Java

  import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
  import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
  import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;

  ...

  GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
      // Specify the CLIENT_ID of the app that accesses the backend:
      .setAudience(Collections.singletonList(CLIENT_ID))
      // Or, if multiple clients access the backend:
      //.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
      .build();

  // (Receive idTokenString by HTTPS POST)

  GoogleIdToken idToken = verifier.verify(idTokenString);
  if (idToken != null) {
    Payload payload = idToken.getPayload();

    // Print user identifier
    String userId = payload.getSubject();
    System.out.println("User ID: " + userId);

    // Get profile information from payload
    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");

    // Use or store profile information
    // ...

  } else {
    System.out.println("Invalid ID token.");
  }

يتم استخدام طريقة GoogleIdTokenVerifier.verify() لإثبات صحة توقيع JWT ومطالبة aud ومطالبة iss ومطالبة المستخدمينexp.

إذا كنت بحاجة إلى التأكّد من أنّ الرمز المميّز للمعرّف يمثّل حساب مؤسسة على Google Workspace أو Cloud، يمكنك تأكيد مطالبة hd من خلال التحقّق من اسم النطاق الذي تم إرجاعه باستخدام طريقة Payload.getHostedDomain().

جارٍ استدعاء نقطة نهاية iconinfo

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

للتحقّق من صحة رمز مميّز للمعرّف باستخدام نقطة النهاية tokeninfo، يمكنك تقديم طلب HTTPS POST أو GET إلى نقطة النهاية، وإدخال الرمز المميّز للمعرّف في معلَمة id_token. على سبيل المثال، للتحقق من الرمز المميز "XYZ123"، قم بإجراء طلب GET التالي:

https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123

إذا تم توقيع الرمز المميّز بشكل صحيح وكانت مطالبتا iss وexp تتضمّنان القيم المتوقّعة، ستتلقّى استجابة HTTP 200، حيث يحتوي النص على مطالبات الرمز المميّز للمعرّف بتنسيق JSON. إليك مثال على الرد:

{
 // These six fields are included in all Google ID Tokens.
 "iss": "https://accounts.google.com",
 "sub": "110169484474386276334",
 "azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
 "iat": "1433978353",
 "exp": "1433981953",

 // These seven fields are only included when the user has granted the "profile" and
 // "email" OAuth scopes to the application.
 "email": "testuser@gmail.com",
 "email_verified": "true",
 "name" : "Test User",
 "picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
 "given_name": "Test",
 "family_name": "User",
 "locale": "en"
}

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