تسجيل الدخول إلى الحساب المرتبط على أجهزة 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.2.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

وتُعدّ مكتبة برامج Java Google API الطريقة الموصى بها للتحقّق من الرموز المميّزة لمعرّفات 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.