Xác thực là quá trình xác định danh tính của một người và thường được gọi là quy trình đăng ký hoặc đăng nhập của người dùng. Uỷ quyền là quá trình cấp hoặc từ chối quyền truy cập vào dữ liệu hoặc tài nguyên. Ví dụ: ứng dụng của bạn yêu cầu người dùng đồng ý truy cập vào Google Drive của họ.
Các lệnh gọi xác thực và uỷ quyền phải là hai luồng riêng biệt dựa trên nhu cầu của ứng dụng.
Trước đây, bạn có thể yêu cầu quyền truy cập vào các phạm vi bổ sung trong quy trình xác thực.Nếu ứng dụng của bạn có các tính năng có thể sử dụng dữ liệu API của Google nhưng không bắt buộc phải có trong các tính năng cốt lõi của ứng dụng, hãy thiết kế ứng dụng để có thể xử lý các trường hợp khi không truy cập được dữ liệu API. Ví dụ: bạn có thể ẩn danh sách các tệp đã lưu gần đây khi người dùng chưa cấp quyền truy cập vào Drive.
Bạn chỉ nên yêu cầu quyền truy cập vào các phạm vi mà bạn cần để truy cập vào API của Google khi người dùng thực hiện một hành động yêu cầu quyền truy cập vào một API cụ thể. Ví dụ: bạn nên yêu cầu quyền truy cập vào Drive của người dùng bất cứ khi nào người dùng nhấn vào nút Lưu vào Drive.
Bằng cách tách biệt quy trình uỷ quyền với quy trình xác thực, bạn có thể tránh làm người dùng mới cảm thấy choáng ngợp hoặc khiến người dùng nhầm lẫn về lý do họ được yêu cầu cấp một số quyền nhất định.
Đối với quy trình xác thực, bạn nên sử dụng API Trình quản lý thông tin xác thực. Để uỷ quyền cho các hành động cần truy cập vào dữ liệu người dùng do Google lưu trữ, bạn nên sử dụng AuthorizationClient.
Thiết lập dự án trên Google Cloud Console
- Mở dự án của bạn trong Cloud Console, hoặc tạo một dự án nếu bạn chưa có.
- Trên trang Thương hiệu,
hãy đảm bảo rằng tất cả thông tin đều đầy đủ và chính xác.
- Đảm bảo rằng ứng dụng của bạn có Tên ứng dụng, Biểu trưng ứng dụng và Trang chủ ứng dụng chính xác được chỉ định. Các giá trị này sẽ được trình bày cho người dùng trên màn hình đồng ý Đăng nhập bằng Google khi đăng ký và màn hình Ứng dụng và dịch vụ của bên thứ ba.
- Đảm bảo rằng bạn đã chỉ định URL của chính sách quyền riêng tư và điều khoản dịch vụ của ứng dụng.
- Trên trang Ứng dụng,
hãy tạo mã ứng dụng Android cho ứng dụng của bạn nếu bạn chưa có. Bạn sẽ cần chỉ định tên gói và chữ ký SHA-1 của ứng dụng.
- Chuyển đến trang Ứng dụng.
- Nhấp vào Tạo ứng dụng.
- Chọn loại ứng dụng Android.
- Nhập tên cho ứng dụng OAuth. Tên này xuất hiện trên trang Ứng dụng của dự án để xác định ứng dụng.
- Nhập tên gói của ứng dụng Android. Giá trị này được xác định trong thuộc tính
packagecủa phần tử<manifest>trong tệpAndroidManifest.xml. - Nhập dấu vân tay chứng chỉ ký SHA-1 của bản phân phối ứng dụng.
- Nếu ứng dụng của bạn sử dụng tính năng ký ứng dụng của Google Play, hãy sao chép dấu vân tay SHA-1 từ trang ký ứng dụng của Play Console.
- Nếu bạn tự quản lý kho khoá và khoá ký, hãy sử dụng tiện ích keytool
có trong Java để in thông tin chứng chỉ ở định dạng mà con người có thể đọc được. Sao chép giá trị
SHA-1trong phầnCertificate fingerprintscủa đầu ra keytool. Xem bài viết Xác thực ứng dụng của bạn trong tài liệu về API của Google cho Android để biết thêm thông tin. - (Không bắt buộc) Xác minh quyền sở hữu ứng dụng Android của bạn.
- Trên trang Ứng dụng,
hãy tạo mã ứng dụng "Ứng dụng web" mới nếu bạn chưa có. Hiện tại, bạn có thể bỏ qua các trường "Nguồn gốc JavaScript được uỷ quyền" và "URI chuyển hướng được uỷ quyền". Mã ứng dụng này sẽ được dùng để xác định máy chủ phụ trợ của bạn khi máy chủ này giao tiếp với các dịch vụ xác thực của Google.
- Chuyển đến trang Ứng dụng.
- Nhấp vào Tạo ứng dụng.
- Chọn loại Ứng dụng web.
Xác minh quyền sở hữu ứng dụng
Bạn có thể xác minh quyền sở hữu ứng dụng để giảm nguy cơ bị mạo danh ứng dụng.
Để hoàn tất quy trình xác minh, bạn có thể sử dụng tài khoản nhà phát triển trên Google Play nếu có và ứng dụng của bạn đã đăng ký trên Google Play Console. Bạn phải đáp ứng các yêu cầu sau để xác minh thành công:
- Bạn phải có một ứng dụng đã đăng ký trong Google Play Console có cùng tên gói và dấu vân tay chứng chỉ ký SHA-1 như ứng dụng OAuth Android mà bạn đang hoàn tất quy trình xác minh.
- Bạn phải có quyền Quản trị viên đối với ứng dụng trong Google Play Console. Tìm hiểu thêm về cách quản lý quyền truy cập trong Google Play Console.
Trong phần Xác minh quyền sở hữu ứng dụng của ứng dụng Android, hãy nhấp vào nút Xác minh quyền sở hữu để hoàn tất quy trình xác minh.
Nếu xác minh thành công, bạn sẽ thấy một thông báo xác nhận quy trình xác minh đã thành công. Nếu không, bạn sẽ thấy lời nhắc lỗi.
Để khắc phục lỗi xác minh, hãy thử các cách sau:
- Đảm bảo rằng ứng dụng mà bạn đang xác minh là một ứng dụng đã đăng ký trong Google Play Console.
- Đảm bảo rằng bạn có quyền Quản trị viên đối với ứng dụng trong Google Play Console.
Khai báo phần phụ thuộc
Trong tệp build.gradle của mô-đun, hãy khai báo các phần phụ thuộc bằng phiên bản mới nhất của thư viện Dịch vụ nhận dạng của Google.
dependencies {
// ... other dependencies
implementation "com.google.android.gms:play-services-auth:21.6.0"
}
Yêu cầu các quyền mà hành động của người dùng cần có
Bất cứ khi nào người dùng thực hiện một hành động yêu cầu phạm vi bổ sung, hãy gọi AuthorizationClient.authorize(). Ví dụ: nếu người dùng thực hiện một hành động yêu cầu quyền truy cập vào bộ nhớ ứng dụng Drive của họ, hãy làm như sau:
Kotlin
val requestedScopes: List<Scope> = listOf(DriveScopes.DRIVE_FILE)
val authorizationRequest = AuthorizationRequest.builder()
.setRequestedScopes(requestedScopes)
.build()
Identity.getAuthorizationClient(activity)
.authorize(authorizationRequestBuilder.build())
.addOnSuccessListener { authorizationResult ->
if (authorizationResult.hasResolution()) {
val pendingIntent = authorizationResult.pendingIntent
// Access needs to be granted by the user
startAuthorizationIntent.launch(IntentSenderRequest.Builder(pendingIntent!!.intentSender).build())
} else {
// Access was previously granted, continue with user action
saveToDriveAppFolder(authorizationResult);
}
}
.addOnFailureListener { e -> Log.e(TAG, "Failed to authorize", e) }
Java
List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_FILE);
AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
.setRequestedScopes(requestedScopes)
.build();
Identity.getAuthorizationClient(activity)
.authorize(authorizationRequest)
.addOnSuccessListener(authorizationResult -> {
if (authorizationResult.hasResolution()) {
// Access needs to be granted by the user
startAuthorizationIntent.launch(
new IntentSenderRequest.Builder(
authorizationResult.getPendingIntent().getIntentSender()
).build()
);
} else {
// Access was previously granted, continue with user action
saveToDriveAppFolder(authorizationResult);
}
})
.addOnFailureListener(e -> Log.e(TAG, "Failed to authorize", e));
Khi xác định ActivityResultLauncher, hãy xử lý phản hồi như trong đoạn mã sau, trong đó chúng tôi giả định rằng thao tác này được thực hiện trong một mảnh. Mã này sẽ kiểm tra để đảm bảo rằng các quyền cần thiết đã được cấp thành công, sau đó thực hiện hành động của người dùng.
Kotlin
private lateinit var startAuthorizationIntent: ActivityResultLauncher<IntentSenderRequest>
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
// ...
startAuthorizationIntent =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
try {
// extract the result
val authorizationResult = Identity.getAuthorizationClient(requireContext())
.getAuthorizationResultFromIntent(activityResult.data)
// continue with user action
saveToDriveAppFolder(authorizationResult);
} catch (e: ApiException) {
// log exception
}
}
}
Java
private ActivityResultLauncher<IntentSenderRequest> startAuthorizationIntent;
@Override
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ...
startAuthorizationIntent =
registerForActivityResult(
new ActivityResultContracts.StartIntentSenderForResult(),
activityResult -> {
try {
// extract the result
AuthorizationResult authorizationResult =
Identity.getAuthorizationClient(requireActivity())
.getAuthorizationResultFromIntent(activityResult.getData());
// continue with user action
saveToDriveAppFolder(authorizationResult);
} catch (ApiException e) {
// log exception
}
});
}
Nếu bạn đang truy cập vào API của Google ở phía máy chủ, hãy gọi phương thức
getServerAuthCode() từ AuthorizationResult để nhận mã
uỷ quyền mà bạn gửi đến phần phụ trợ để trao đổi mã truy cập và
mã làm mới. Để tìm hiểu thêm, hãy xem
bài viết Duy trì quyền truy cập liên tục vào dữ liệu của người dùng.
Thu hồi quyền truy cập vào dữ liệu người dùng hoặc tài nguyên
Để thu hồi quyền truy cập đã cấp trước đó, hãy gọi
AuthorizationClient.revokeAccess(). Ví dụ: nếu người dùng đang xoá tài khoản của họ khỏi ứng dụng của bạn và ứng dụng của bạn đã được cấp quyền truy cập vào DriveScopes.DRIVE_FILE, hãy sử dụng mã sau để thu hồi quyền truy cập:
Kotlin
val requestedScopes: MutableList<Scope> = mutableListOf(DriveScopes.DRIVE_FILE)
RevokeAccessRequest revokeAccessRequest = RevokeAccessRequest.builder()
.setAccount(account)
.setScopes(requestedScopes)
.build()
Identity.getAuthorizationClient(activity)
.revokeAccess(revokeAccessRequest)
.addOnSuccessListener { Log.i(TAG, "Successfully revoked access") }
.addOnFailureListener { e -> Log.e(TAG, "Failed to revoke access", e) }
Java
List<Scopes> requestedScopes = Arrays.asList(DriveScopes.DRIVE_FILE);
RevokeAccessRequest revokeAccessRequest = RevokeAccessRequest.builder()
.setAccount(account)
.setScopes(requestedScopes)
.build();
Identity.getAuthorizationClient(activity)
.revokeAccess(revokeAccessRequest)
.addOnSuccessListener(unused -> Log.i(TAG, "Successfully revoked access"))
.addOnFailureListener(e -> Log.e(TAG, "Failed to revoke access", e));
Xoá bộ nhớ đệm mã thông báo
Mã truy cập OAuth được lưu vào bộ nhớ đệm cục bộ khi nhận được từ máy chủ, giúp tăng tốc độ truy cập và giảm số lệnh gọi mạng. Các mã thông báo này sẽ tự động bị xoá khỏi bộ nhớ đệm khi hết hạn, nhưng cũng có thể trở nên không hợp lệ vì các lý do khác.
Nếu bạn nhận được IllegalStateException khi sử dụng mã thông báo, hãy xoá bộ nhớ đệm cục bộ để đảm bảo rằng yêu cầu uỷ quyền tiếp theo cho mã truy cập sẽ chuyển đến máy chủ OAuth. Đoạn mã sau sẽ xoá invalidAccessToken khỏi bộ nhớ đệm cục bộ:
Kotlin
Identity.getAuthorizationClient(activity)
.clearToken(ClearTokenRequest.builder().setToken(invalidAccessToken).build())
.addOnSuccessListener { Log.i(TAG, "Successfully removed the token from the cache") }
.addOnFailureListener{ e -> Log.e(TAG, "Failed to clear token", e) }
Java
Identity.getAuthorizationClient(activity)
.clearToken(ClearTokenRequest.builder().setToken(invalidAccessToken).build())
.addOnSuccessListener(unused -> Log.i(TAG, "Successfully removed the token from the cache"))
.addOnFailureListener(e -> Log.e(TAG, "Failed to clear the token cache", e));
Nhận thông tin người dùng trong quá trình uỷ quyền
Phản hồi uỷ quyền không chứa bất kỳ thông tin nào về tài khoản người dùng đã sử dụng; phản hồi chỉ chứa mã thông báo cho các phạm vi được yêu cầu. Ví dụ: phản hồi để lấy mã truy cập vào Google Drive của người dùng không tiết lộ danh tính của tài khoản mà người dùng đã chọn, mặc dù có thể dùng mã này để truy cập vào các tệp trên Drive của người dùng. Để nhận thông tin như tên hoặc email của người dùng, bạn có các lựa chọn sau:
Đăng nhập cho người dùng bằng Tài khoản Google của họ bằng API Trình quản lý thông tin xác thực trước khi yêu cầu uỷ quyền. Phản hồi xác thực từ Trình quản lý thông tin xác thực bao gồm thông tin người dùng như địa chỉ email và cũng đặt tài khoản mặc định của ứng dụng thành tài khoản đã chọn; nếu cần, bạn có thể theo dõi tài khoản này trong ứng dụng của mình. Yêu cầu uỷ quyền tiếp theo sẽ sử dụng tài khoản này làm tài khoản mặc định và bỏ qua bước chọn tài khoản trong quy trình uỷ quyền. Để sử dụng một tài khoản khác cho quy trình uỷ quyền, hãy xem bài viết Uỷ quyền từ một tài khoản không phải là tài khoản mặc định.
Trong yêu cầu uỷ quyền, ngoài các phạm vi mà bạn muốn (ví dụ:
Drive scope), hãy yêu cầu các phạm viuserinfo,profilevàopenid. Sau khi mã truy cập được trả về, hãy nhận thông tin người dùng bằng cách thực hiện yêu cầu HTTPGETđến điểm cuối userinfo OAuth (https://www.googleapis.com/oauth2/v3/userinfo) bằng thư viện HTTP mà bạn muốn và đưa mã truy cập mà bạn đã nhận được vào tiêu đề, tương đương với lệnhcurlsau:curl -X GET \ "https://www.googleapis.com/oauth2/v1/userinfo?alt=json" \ -H "Authorization: Bearer $TOKEN"Phản hồi là
UserInfo, chỉ giới hạn ở các phạm vi đã được yêu cầu, được định dạng ở JSON.
Uỷ quyền từ một tài khoản không phải là tài khoản mặc định
Nếu bạn sử dụng Trình quản lý thông tin xác thực để xác thực và chạy
AuthorizationClient.authorize(), thì tài khoản mặc định của ứng dụng sẽ được đặt thành
tài khoản mà người dùng của bạn đã chọn. Điều này có nghĩa là mọi lệnh gọi tiếp theo cho quy trình uỷ quyền đều sử dụng tài khoản mặc định này. Để buộc hiển thị bộ chọn tài khoản,
hãy đăng xuất người dùng khỏi ứng dụng bằng API clearCredentialState() của
Trình quản lý thông tin xác thực.
Duy trì quyền truy cập liên tục vào dữ liệu của người dùng
Nếu bạn cần truy cập vào dữ liệu của người dùng từ ứng dụng của mình, hãy gọi
AuthorizationClient.authorize() một lần; trong các phiên tiếp theo và miễn là người dùng không xoá các quyền đã cấp, hãy gọi cùng một phương thức để lấy mã truy cập nhằm đạt được mục tiêu của bạn mà không cần người dùng tương tác. Mặt khác, nếu bạn cần truy cập vào dữ liệu của người dùng ở chế độ ngoại tuyến, từ máy chủ phụ trợ, thì bạn cần yêu cầu một loại mã thông báo khác có tên là "mã làm mới".
Mã truy cập được thiết kế có chủ đích để có thời gian tồn tại ngắn và có thời gian tồn tại là một giờ. Nếu mã truy cập bị chặn hoặc bị xâm nhập, thì khoảng thời gian hợp lệ có giới hạn sẽ giảm thiểu khả năng bị sử dụng sai. Sau khi hết hạn, mã thông báo sẽ trở nên không hợp lệ và mọi nỗ lực sử dụng mã này sẽ bị máy chủ tài nguyên từ chối. Vì mã truy cập có thời gian tồn tại ngắn, nên máy chủ sử dụng mã thông báo làm mới để duy trì quyền truy cập liên tục vào dữ liệu của người dùng. Mã thông báo làm mới là mã thông báo có thời gian tồn tại dài mà ứng dụng sử dụng để yêu cầu mã truy cập có thời gian tồn tại ngắn từ máy chủ uỷ quyền, khi mã truy cập cũ đã hết hạn mà không cần lượt tương tác của người dùng.
Để lấy mã làm mới, trước tiên, bạn cần lấy mã uỷ quyền (hoặc mã uỷ quyền) trong bước uỷ quyền trong ứng dụng của mình bằng cách yêu cầu "quyền truy cập ngoại tuyến", sau đó trao đổi mã uỷ quyền để lấy mã làm mới trên máy chủ của bạn. Bạn phải lưu trữ mã thông báo làm mới có thời gian tồn tại dài một cách an toàn trên máy chủ của mình vì bạn có thể sử dụng mã này nhiều lần để lấy mã truy cập mới. Do đó, bạn không nên lưu trữ mã thông báo làm mới trên thiết bị vì lo ngại về vấn đề bảo mật. Thay vào đó, bạn nên lưu trữ mã này trong các máy chủ phụ trợ của ứng dụng nơi diễn ra quá trình trao đổi để lấy mã truy cập.
Sau khi mã uỷ quyền được gửi đến máy chủ phụ trợ của ứng dụng, bạn có thể trao đổi mã này để lấy mã truy cập có thời gian tồn tại ngắn trên máy chủ và mã làm mới có thời gian tồn tại dài bằng cách làm theo các bước trong hướng dẫn uỷ quyền tài khoản. Quá trình trao đổi này chỉ nên diễn ra ở phần phụ trợ của ứng dụng.
Kotlin
// Ask for offline access during the first authorization request
val authorizationRequest = AuthorizationRequest.builder()
.setRequestedScopes(requestedScopes)
.requestOfflineAccess(serverClientId)
.build()
Identity.getAuthorizationClient(activity)
.authorize(authorizationRequest)
.addOnSuccessListener { authorizationResult ->
startAuthorizationIntent.launch(IntentSenderRequest.Builder(
pendingIntent!!.intentSender
).build())
}
.addOnFailureListener { e -> Log.e(TAG, "Failed to authorize", e) }
Java
// Ask for offline access during the first authorization request
AuthorizationRequest authorizationRequest = AuthorizationRequest.builder()
.setRequestedScopes(requestedScopes)
.requestOfflineAccess(serverClientId)
.build();
Identity.getAuthorizationClient(getContext())
.authorize(authorizationRequest)
.addOnSuccessListener(authorizationResult -> {
startAuthorizationIntent.launch(
new IntentSenderRequest.Builder(
authorizationResult.getPendingIntent().getIntentSender()
).build()
);
})
.addOnFailureListener(e -> Log.e(TAG, "Failed to authorize"));
Đoạn mã sau giả định rằng quy trình uỷ quyền được bắt đầu từ một mảnh.
Kotlin
private lateinit var startAuthorizationIntent: ActivityResultLauncher<IntentSenderRequest>
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
// ...
startAuthorizationIntent =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { activityResult ->
try {
val authorizationResult = Identity.getAuthorizationClient(requireContext())
.getAuthorizationResultFromIntent(activityResult.data)
// short-lived access token
accessToken = authorizationResult.accessToken
// store the authorization code used for getting a refresh token safely to your app's backend server
val authCode: String = authorizationResult.serverAuthCode
storeAuthCodeSafely(authCode)
} catch (e: ApiException) {
// log exception
}
}
}
Java
private ActivityResultLauncher<IntentSenderRequest> startAuthorizationIntent;
@Override
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ...
startAuthorizationIntent =
registerForActivityResult(
new ActivityResultContracts.StartIntentSenderForResult(),
activityResult -> {
try {
AuthorizationResult authorizationResult =
Identity.getAuthorizationClient(requireActivity())
.getAuthorizationResultFromIntent(activityResult.getData());
// short-lived access token
accessToken = authorizationResult.getAccessToken();
// store the authorization code used for getting a refresh token safely to your app's backend server
String authCode = authorizationResult.getServerAuthCode()
storeAuthCodeSafely(authCode);
} catch (ApiException e) {
// log exception
}
});
}