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

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

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

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

آلية العمل

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

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

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

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

  1. افتح إدارة حزمة تطوير البرامج (SDK) لنظام التشغيل Android
  1. ضمن أدوات حزمة تطوير البرامج (SDK)، ابحث عن خدمات Google Play.

  2. إذا كانت حالة هذه الحزم غير مثبّتة، فحدد كلتيهما وانقر على تثبيت الحِزم:

إعداد تطبيقك

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

    buildscript {
        repositories {
            google()
        }
    }
    
    allprojects {
        repositories {
            google()
        }
    }
    
  2. إضافة الموارد التابعة لـ "الربط بـ Google" واجهة برمجة التطبيقات (API) في الوحدة ملف 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);
    });
    

التأكّد من صحة الرمز المميّز للمعرّف

如需验证令牌是否有效,请确保: 满足条件:

  • ID 令牌已由 Google 正确签名。使用 Google 的公钥 (适用于 JWKPEM 格式) 以验证令牌的签名。这些密钥会定期轮替:检查 响应中的 Cache-Control 标头来确定何时 您应该再次检索它们。
  • ID 令牌中 aud 的值等于您应用的某个 客户端 ID必须进行此检查,以防止向恶意 应用用于访问应用的后端服务器上的同一用户的相关数据。
  • ID 令牌中 iss 的值等于 accounts.google.comhttps://accounts.google.com
  • ID 令牌的过期时间 (exp) 未过。
  • 如果您需要验证 ID 令牌是否代表 Google Workspace 或 Cloud 组织账号时,您可以查看 hd 声明,该声明表示托管的 用户的网域。将资源访问权限限制为只有 特定网域。缺少此声明即表示相应账号不属于 Google 托管的域名。

您可以使用 emailemail_verifiedhd 字段来确定 Google 负责托管电子邮件地址,并对其具有权威性。如果 Google 拥有权威性, 该用户就是所知的合法账号所有者,您可以跳过密码或其他输入框 验证方法。

Google 具有权威性的情形:

  • email 的后缀为 @gmail.com,这是一个 Gmail 账号。
  • email_verified 为 true 且 hd 已设置,这是 G Suite 账号。

用户无需使用 Gmail 或 G Suite 即可注册 Google 账号。时间 email 不包含 @gmail.com 后缀且 hd 不存在,Google 未 建议使用权威凭据和密码或其他验证方法进行验证 用户。email_verified 可能为 true,因为 Google 最初验证了 创建 Google 账号后,该用户会拥有第三方的所有权, 后,电子邮件账号可能已更改。

我们强烈建议不要自行编写代码来执行这些验证步骤, 建议您使用适用于您平台的 Google API 客户端库, JWT 库。对于开发和调试,您可以调用我们的 tokeninfo 验证端点。

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

إنّ استخدام مكتبة Java Google API Client Library هو الطريقة المقترَحة للتحقّق من صحة رموز تعريف 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().

调用 tokeninfo 端点

调试验证 ID 令牌签名的一种简单方法是 使用 tokeninfo 端点。调用此端点涉及 这个额外的网络请求会为您完成大部分的验证工作, 验证和载荷提取。不适合在生产环境中使用 因为请求可能会受到限制或出现间歇性错误。

如需使用 tokeninfo 端点验证 ID 令牌,请创建 HTTPS POST 或 GET 请求发送到端点,并在 id_token 参数。 例如,要验证令牌“XYZ123”,请发出以下 GET 请求:

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

如果令牌经过正确签名,并且 issexp 具有预期值,就会收到 HTTP 200 响应,其中正文 包含 JSON 格式的 ID 令牌声明。 以下是示例响应:

{
 // 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"
}

如果您需要验证 ID 令牌是否代表 Google Workspace 账号,可以先查看 hd 声明,指示用户的托管网域。只有在以下情况下, 从而仅允许特定网域中的成员访问资源。缺少此声明 表示该账号不属于 Google Workspace 托管网域。