Đăng nhập bằng tài khoản được liên kết dành cho Android

Tính năng Đăng nhập bằng tài khoản được liên kết sẽ bật tính năng Đăng nhập bằng một lần chạm bằng Google đối với những người dùng đã liên kết Tài khoản Google của họ với dịch vụ của bạn. Điều này giúp người dùng có trải nghiệm tốt hơn vì họ có thể đăng nhập bằng một lần nhấp mà không cần nhập lại tên người dùng và mật khẩu. Việc này cũng giúp giảm khả năng người dùng tạo tài khoản trùng lặp trên dịch vụ của bạn.

Tính năng Đăng nhập bằng tài khoản được liên kết hiện có trong quy trình Đăng nhập bằng một lần chạm dành cho Android. Điều này có nghĩa là bạn không cần nhập thư viện riêng nếu ứng dụng của bạn đã bật tính năng Một lần chạm.

Trong tài liệu này, bạn sẽ tìm hiểu cách sửa đổi ứng dụng Android để hỗ trợ tính năng Đăng nhập bằng tài khoản được liên kết.

Cách thức hoạt động

  1. Bạn chọn hiển thị các tài khoản được liên kết trong quy trình Đăng nhập bằng một lần chạm.
  2. Nếu người dùng đăng nhập trên Google và đã liên kết Tài khoản Google của họ với tài khoản của họ trên dịch vụ của bạn, thì một mã thông báo mã nhận dạng sẽ được trả về cho tài khoản được liên kết.
  3. Người dùng sẽ nhìn thấy lời nhắc đăng nhập bằng một lần chạm cùng với lựa chọn đăng nhập vào dịch vụ của bạn bằng tài khoản được liên kết.
  4. Nếu người dùng chọn tiếp tục với tài khoản được liên kết, mã thông báo mã nhận dạng của người dùng đó sẽ được trả về ứng dụng của bạn. Bạn so khớp mã này với mã thông báo đã được gửi đến máy chủ ở bước 2 để xác định người dùng đã đăng nhập.

Thiết lập

Thiết lập môi trường phát triển

Tải các dịch vụ mới nhất của Google Play trên máy chủ phát triển của bạn:

  1. Mở Trình quản lý SDK Android.
  1. Trong phần SDK Tools (Bộ công cụ SDK), hãy tìm Google Play Services (Dịch vụ Google Play).

  2. Nếu trạng thái của các gói này là chưa cài đặt, hãy chọn cả hai rồi nhấp vào Install Package (Cài đặt gói).

Định cấu hình ứng dụng

  1. Trong tệp build.gradle cấp dự án, hãy đưa kho lưu trữ Maven của Google vào cả hai phần buildscriptallprojects.

    buildscript {
        repositories {
            google()
        }
    }
    
    allprojects {
        repositories {
            google()
        }
    }
    
  2. Thêm các phần phụ thuộc cho API "Link with Google" (Liên kết với Google) vào tệp gradle cấp ứng dụng trong mô-đun của bạn, thường là app/build.gradle:

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

Sửa đổi ứng dụng Android để hỗ trợ tính năng Đăng nhập bằng tài khoản được liên kết

Vào cuối quy trình Đăng nhập bằng tài khoản được liên kết, một mã thông báo mã nhận dạng sẽ được trả về ứng dụng của bạn. Bạn phải xác minh tính toàn vẹn của mã thông báo mã nhận dạng trước khi đăng nhập cho người dùng.

Mã mẫu sau đây trình bày chi tiết các bước truy xuất, xác minh mã thông báo giá trị nhận dạng, sau đó đăng nhập cho người dùng.

  1. Tạo một hoạt động để nhận kết quả của ý định đăng nhập

    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. Tạo yêu cầu đăng nhập

    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. Chạy ý định đang chờ đăng nhập

    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);
    });
    

Xác minh tính toàn vẹn của mã thông báo giá trị nhận dạng

Để xác minh rằng mã thông báo là hợp lệ, hãy đảm bảo đáp ứng các tiêu chí sau:

  • Google đã ký mã thông báo nhận dạng đúng cách. Bạn có thể dùng khoá công khai của Google (có sẵn ở định dạng JWK hoặc PEM) để xác minh chữ ký của mã thông báo. Các khoá này thường xuyên được xoay vòng; hãy kiểm tra tiêu đề Cache-Control trong phản hồi để xác định thời điểm bạn nên truy xuất lại các khoá.
  • Giá trị của aud trong mã thông báo mã nhận dạng bằng với một trong các mã ứng dụng khách của ứng dụng. Quy trình kiểm tra này là cần thiết để ngăn chặn hành vi dùng mã thông báo mã nhận dạng được cấp cho một ứng dụng độc hại để truy cập vào dữ liệu về cùng một người dùng trên máy chủ phụ trợ của ứng dụng.
  • Giá trị của iss trong mã thông báo mã nhận dạng bằng accounts.google.com hoặc https://accounts.google.com.
  • Chưa qua thời gian hết hạn (exp) của mã thông báo giá trị nhận dạng.
  • Nếu cần xác thực rằng mã thông báo nhận dạng đại diện cho tài khoản Google Workspace hoặc tài khoản tổ chức Cloud, bạn có thể kiểm tra thông báo xác nhận quyền sở hữu hd, trong đó cho biết miền được lưu trữ của người dùng. Bạn phải dùng tính năng này khi chỉ cho phép các thành viên của một số miền nhất định truy cập vào tài nguyên. Việc không có xác nhận quyền sở hữu này cho thấy tài khoản không thuộc một miền do Google lưu trữ.

Thay vì tự viết mã để thực hiện các bước xác minh này, bạn nên sử dụng thư viện ứng dụng API của Google cho nền tảng của mình hoặc thư viện JWT đa năng. Để phát triển và gỡ lỗi, bạn có thể gọi điểm cuối xác thực tokeninfo.

Sử dụng Thư viện ứng dụng API của Google

Bạn nên sử dụng Thư viện ứng dụng Java API của Google để xác thực mã thông báo mã nhận dạng của Google trong môi trường thực tế.

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.");
  }

Phương thức GoogleIdTokenVerifier.verify() xác minh chữ ký JWT, thông báo xác nhận quyền sở hữu aud, thông báo xác nhận quyền sở hữu iss và thông báo xác nhận quyền sở hữuexp.

Nếu cần xác thực rằng mã thông báo nhận dạng đại diện cho tài khoản của tổ chức Google Workspace hoặc Cloud, thì bạn có thể xác minh thông báo xác nhận quyền sở hữu hd bằng cách kiểm tra tên miền mà phương thức Payload.getHostedDomain() trả về.

Gọi điểm cuối tokeninfo

Một cách dễ dàng để xác thực chữ ký mã thông báo mã nhận dạng cho việc gỡ lỗi là sử dụng điểm cuối tokeninfo. Việc gọi điểm cuối này sẽ bao gồm một yêu cầu mạng bổ sung giúp thực hiện hầu hết quy trình xác thực cho bạn trong khi bạn kiểm thử việc xác thực và trích xuất tải trọng phù hợp trong mã của riêng bạn. API này không phù hợp để sử dụng trong mã phát hành chính thức vì yêu cầu có thể bị điều tiết hoặc gặp lỗi không liên tục.

Để xác thực mã thông báo mã nhận dạng bằng điểm cuối tokeninfo, hãy gửi yêu cầu POST hoặc GET HTTPS đến điểm cuối rồi chuyển mã thông báo mã nhận dạng của bạn vào tham số id_token. Ví dụ: để xác thực mã thông báo "XYZ123", hãy thực hiện yêu cầu GET sau:

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

Nếu mã thông báo được ký đúng cách cũng như các thông báo xác nhận quyền sở hữu issexp có giá trị dự kiến, thì bạn sẽ nhận được phản hồi HTTP 200, trong đó phần nội dung chứa các thông báo xác nhận quyền sở hữu mã thông báo có định dạng JSON. Sau đây là câu trả lời mẫu:

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

Nếu cần xác thực rằng mã thông báo nhận dạng đại diện cho một tài khoản Google Workspace, bạn có thể kiểm tra thông báo xác nhận quyền sở hữu hd. Thông báo này cho biết miền mà người dùng lưu trữ. Bạn phải dùng tính năng này khi chỉ cho phép các thành viên của một số miền nhất định truy cập vào tài nguyên. Nếu không có khiếu nại này, tài khoản đó không thuộc một miền do Google Workspace lưu trữ.