Credential Transfer API của Trình quản lý thông tin xác thực cho phép chuyển thông tin xác thực của người dùng một cách an toàn giữa các trình cung cấp thông tin xác thực trên cùng một thiết bị. Hướng dẫn này trình bày chi tiết cách trình cung cấp thông tin xác thực trên Android có thể tích hợp với các API do thư viện androidx.credentials:providerevents cung cấp. Tính năng này hỗ trợ mật khẩu, khoá truy cập, thông tin địa chỉ và các trường tuỳ chỉnh bằng cách sử dụng Định dạng trao đổi thông tin xác thực (CXF) theo tiêu chuẩn FIDO.
Các khái niệm chính
Khung chuyển thông tin xác thực hỗ trợ việc chuyển thông tin xác thực ngang hàng trên cùng một thiết bị mà không để lộ thông tin xác thực thô cho hệ điều hành Android hoặc các ứng dụng chưa được xác thực.
Khung này xác định 2 vai trò chính:
- Trình xuất (nhà cung cấp nguồn): Một trình cung cấp thông tin xác thực hiện đang lưu giữ thông tin đăng nhập của người dùng. Thư viện này đăng ký trước siêu dữ liệu về các tài khoản có thể xuất (
ExportEntry) với hệ thống và phản hồi các yêu cầu chuyển khi người dùng chọn. - Trình nhập (trình cung cấp ứng dụng hoặc trình hướng dẫn thiết lập): Trình cung cấp thông tin đăng nhập hoặc trình hướng dẫn thiết lập sẽ bắt đầu một yêu cầu nhập (
ImportCredentialsRequest) chỉ định những loại thông tin đăng nhập và tiện ích mà trình nhập có thể nhận.
Khả năng tương thích với phiên bản Android
Credentials Transfer API hoạt động trên các thiết bị chạy Android 8 (cấp độ API 26) trở lên.
Thêm phần phụ thuộc
Thêm phần phụ thuộc androidx.credentials:providerevents vào build.gradle hoặc build.gradle.kts của mô-đun:
dependencies {
implementation("androidx.credentials:providerevents:1.0.0-alpha06")
}
Tạo thực thể cho lớp bắt buộc
Tạo một thực thể của ProviderEventsManager bắt buộc.
val providerEventsManager = ProviderEventsManager.create(context)
Triển khai trình xuất
Để cho phép người dùng xuất thông tin đăng nhập từ ứng dụng của bạn sang các trình cung cấp thông tin đăng nhập khác trên thiết bị, hãy triển khai vai trò trình xuất.
Đăng ký các mục xuất
Khi trình cung cấp thông tin đăng nhập của bạn thay đổi (ví dụ: người dùng đăng nhập, thêm thông tin đăng nhập hoặc sửa đổi tài khoản), hãy đăng ký hoặc cập nhật các mục ExportEntry bằng ProviderEventsManager.registerExport().
Mỗi ExportEntry yêu cầu:
id: Một giá trị nhận dạng chuỗi bí mật, được tạo ngẫu nhiên, đại diện duy nhất cho mục nhập xuất này. Bạn phải lưu trữ mã nhận dạng này một cách an toàn; bạn sẽ cần mã nhận dạng này sau này để xác minh các yêu cầu chuyển tiền đến.accountDisplayName: Nhãn tài khoản không bắt buộc (ví dụ:"Personal Account").userDisplayName: Giá trị nhận dạng chính của người dùng (ví dụ:"alice@example.com").icon: Biểu tượngBitmapđại diện cho nhà cung cấp hoặc tài khoản (thư viện tự động điều chỉnh tỷ lệ thành PNG 32x32).supportedCredentialTypes: Một tập hợp các hằng số chuỗi từCredentialTypesbiểu thị những loại mà mục này chứa.
suspend fun registerMyProviderForExport( providerEventsManager: ProviderEventsManager, providerIcon: Bitmap, // Randomly generated and stored in encrypted storage secretEntryId: String ) { val entry = ExportEntry( id = secretEntryId, accountDisplayName = "MyProvider Personal", userDisplayName = "alice@example.com", icon = providerIcon, supportedCredentialTypes = setOf( CredentialTypes.CREDENTIAL_TYPE_BASIC_AUTH, // Passwords CredentialTypes.CREDENTIAL_TYPE_PUBLIC_KEY, // Passkeys CredentialTypes.CREDENTIAL_TYPE_ADDRESS, CredentialTypes.CREDENTIAL_TYPE_CREDIT_CARD ) ) // RegisterExportRequest.create() attaches the default WASM matcher from assets val request = RegisterExportRequest.create(context, listOf(entry)) try { val response = providerEventsManager.registerExport(request) // Registration successful } catch (e: Exception) { // Handle registration exceptions (e.g., RegisterExportProviderConfigurationException) } }
Khai báo hoạt động của trình xuất trong tệp kê khai
Khi người dùng chọn ExportEntry của bạn trên giao diện người dùng của bộ chọn hệ thống, hệ thống sẽ khởi chạy Activity xử lý được chỉ định của bạn. Khai báo hoạt động này bằng thao tác theo ý định và lược đồ URI nội dung bắt buộc:
<activity
android:name="com.example.CredentialExportActivity"
android:exported="true"
android:label="@string/export_activity_label">
<intent-filter>
// This intent action is required for Credential Manager to invoke this activity
<action android:name="androidx.identitycredentials.action.IMPORT_CREDENTIALS" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content" />
</intent-filter>
</activity>
Xử lý ý định chuyển trong hoạt động của bạn
Trong CredentialExportActivity, hãy dùng IntentHandler.retrieveProviderImportCredentialsRequest(intent) để phân tích cú pháp yêu cầu, xác minh ứng dụng gọi và credId, thực hiện mọi quy trình xác thực bằng sinh trắc học bắt buộc và ghi tải trọng FIDO CXF trở lại URI nội dung đã cung cấp:
class CredentialExportActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 1. Extract the transfer request from the incoming Intent val request: ProviderImportCredentialsRequest? = IntentHandler.retrieveProviderImportCredentialsRequest(intent) if (request == null) { finishWithError() return } // 2. Validate CallingAppInfo and secret `credId` val callingAppPackage = request.callingAppInfo.packageName val receivedCredId = request.credId if (!verifySecretEntryId(receivedCredId) || !isTrustedImporter(callingAppPackage)) { // Secret ID mismatch or untrusted caller -> abort sendExceptionAndFinish(ImportCredentialsNoExportOptionException("Unauthorized request")) return } // 3. Optional: Prompt user for Biometric / PIN authentication before exporting authenticateUserThenExport(request) } private fun authenticateUserThenExport(request: ProviderImportCredentialsRequest) { // ... Biometric prompt logic ... // Once authenticated, generate the FIDO CXF JSON string matching the requested types val cxfJsonPayload = buildFidoCxfJsonPayload( requestedTypes = request.request.credentialTypes, requestedExtensions = request.request.knownExtensions ) val response = ImportCredentialsResponse(cxfJsonPayload) // 4. Write the JSON payload to the Content URI and set Activity result IntentHandler.setImportCredentialsResponse( context = this, uri = request.uri, intent = intent, response = response ) setResult(Activity.RESULT_OK, intent) finish() } private fun sendExceptionAndFinish(exception: androidx.credentials.providerevents.exception.ImportCredentialsException) { IntentHandler.setImportCredentialsException(intent, exception) setResult(Activity.RESULT_OK, intent) finish() } private fun verifySecretEntryId(credentialId: String): Boolean { // Check if credentialId matches what you stored when calling RegisterExportRequest return credentialId == getStoredSecretEntryId() } private fun isTrustedImporter(packageName: String): Boolean { // Implement any specific allowlisting / caller checks if required return true } private fun finishWithError() { setResult(Activity.RESULT_CANCELED) finish() } }
Xuất thông tin đăng ký và xoá các trường hợp ngoại lệ
Tất cả các trường hợp ngoại lệ trong mô-đun này đều là lớp con của ImportCredentialsException, RegisterExportException hoặc ClearExportException.
RegisterExportProviderConfigurationExceptionhoặcClearExportProviderConfigurationException: Xuất hiện khi quá trình đăng ký hoặc xoá không thành công do các vấn đề về thiết lập nhà cung cấp (ví dụ:supportedCredentialTypestrống trongExportEntryhoặc gọi trên một cấp hệ điều hành không được hỗ trợ).RegisterExportUnknownErrorExceptionhoặcClearExportUnknownErrorException: Đã xảy ra lỗi hệ thống hoặc lỗi lưu trữ chưa phân loại trong khi cập nhật sổ đăng ký xuất.
Triển khai trình nhập
Để nhập thông tin đăng nhập vào ứng dụng (ví dụ: trong quá trình giới thiệu hoặc nhập nhà cung cấp), hãy triển khai vai trò trình nhập và bắt đầu quy trình bằng cách gọi ProviderEventsManager.importCredentials().
Tạo yêu cầu nhập và quy trình khởi chạy
Chỉ định CredentialTypes và KnownExtensions mà trình nhập của bạn hỗ trợ:
suspend fun startCredentialImport( activityContext: Context, providerEventsManager: ProviderEventsManager ) { val importRequest = ImportCredentialsRequest( credentialTypes = setOf( CredentialTypes.CREDENTIAL_TYPE_BASIC_AUTH, CredentialTypes.CREDENTIAL_TYPE_PUBLIC_KEY, CredentialTypes.CREDENTIAL_TYPE_ADDRESS, CredentialTypes.CREDENTIAL_TYPE_NOTE ), knownExtensions = setOf( KnownExtensions.KNOWN_EXTENSION_SHARED ) ) try { // Launches the system Selector UI; suspends until user selects a provider and completes transfer val response = providerEventsManager.importCredentials(activityContext, importRequest) // 1. Inspect the source exporter's package info val exporterPackageName = response.callingAppInfo.packageName // 2. Parse the FIDO CXF JSON string val cxfJsonString = response.response.responseJson parseAndSaveImportedCredentials(cxfJsonString) } catch (e: ImportCredentialsException) { // Handle specific import exceptions (e.g., ImportCredentialsCancellationException) handleImportFailure(e) } } private fun parseAndSaveImportedCredentials(cxfJsonString: String) { val rootJson = JSONObject(cxfJsonString) // Parse according to FIDO Credential Exchange Format (CXF v1.0) specification: // https://fidoalliance.org/specs/cx/cxf-v1.0-ps-20250814.html } // Helper function to make it compile private fun handleImportFailure(e: ImportCredentialsException) {}
Nhập các ngoại lệ về luồng
Tất cả các trường hợp ngoại lệ trong mô-đun này đều là lớp con của ImportCredentialsException, RegisterExportException hoặc ClearExportException.
ImportCredentialsCancellationException: Người dùng đã đóng giao diện người dùng của bộ chọn hoặc huỷ hoạt động xuất (Activity.RESULT_CANCELED).ImportCredentialsNoExportOptionException: Không có mục nhập xuất đã đăng ký nào khớp với các loại thông tin đăng nhập được yêu cầu hoặc trình xuất đã chọn đưa ra một ngoại lệ từ chối.ImportCredentialsProviderConfigurationException: Lỗi cấu hình (ví dụ: tập hợpcredentialTypestrống trongImportCredentialsRequesthoặc thiếu quyền).ImportCredentialsInvalidJsonException: Trình xuất trả về một tải trọng JSON bị lỗi hoặc trống, không xác thực được yêu cầu.ImportCredentialsSystemErrorException: Đã xảy ra lỗi chuyển Binder hoặc lỗi hệ thống Android nội bộ trong quá trình nhập.ImportCredentialsUnknownCallerException: Khung không xác minh được ứng dụng gọi điện.ImportCredentialsUnknownErrorException: Lỗi chưa phân loại hoặc không mong muốn trong quá trình nhập.
Các loại thông tin đăng nhập và tiện ích được hỗ trợ
Đối tượng androidx.credentials.providerevents.transfer.CredentialTypes xác định các hằng số chuỗi được chuẩn hoá tương ứng với các loại mục FIDO CXF:
| Hằng số | Giá trị (loại cxf) |
Mô tả |
|---|---|---|
CREDENTIAL_TYPE_BASIC_AUTH |
"basic-auth" |
Thông tin đăng nhập bằng tên người dùng và mật khẩu. |
CREDENTIAL_TYPE_PUBLIC_KEY |
"passkey" |
Thông tin xác thực khoá công khai khoá truy cập FIDO2 hoặc WebAuthn. |
CREDENTIAL_TYPE_ADDRESS |
"address" |
Thông tin về địa chỉ bưu điện hoặc địa chỉ giao hàng để tự động điền vào biểu mẫu. |
CREDENTIAL_TYPE_API_KEY |
"api-key" |
Khoá và mã thông báo truy cập API. |
CREDENTIAL_TYPE_CREDIT_CARD |
"credit-card" |
Thông tin thanh toán bằng thẻ tín dụng và thẻ ghi nợ. |
CREDENTIAL_TYPE_CUSTOM_FIELDS |
"custom-fields" |
Nhóm tuỳ chỉnh hoặc trường do người dùng xác định. |
CREDENTIAL_TYPE_DRIVERS_LICENSE |
"drivers-license" |
Thông tin chi tiết về giấy phép lái xe. |
CREDENTIAL_TYPE_FILE |
"file" |
Siêu dữ liệu và phần giữ chỗ tham chiếu cho tệp nhị phân. |
CREDENTIAL_TYPE_GENERATED_PASSWORD |
"generated-password" |
Mật khẩu an toàn do máy tạo. |
CREDENTIAL_TYPE_IDENTITY_DOCUMENT |
"identity-document" |
Thẻ căn cước, số an sinh xã hội, mã số thuế hoặc thông tin hộ chiếu. |
CREDENTIAL_TYPE_ITEM_REFERENCE |
"item-reference" |
Các đường liên kết logic trỏ đến một mục khác trong tải trọng. |
CREDENTIAL_TYPE_NOTE |
"note" |
Ghi chú bảo mật do người dùng xác định (chuỗi UTF-8). |
CREDENTIAL_TYPE_PASSPORT |
"passport" |
Thông tin về hộ chiếu và giấy tờ tuỳ thân khi đi lại. |
CREDENTIAL_TYPE_PERSON_NAME |
"person-name" |
Thông tin nhận dạng và tên của người. |
CREDENTIAL_TYPE_SSH_KEY |
"ssh-key" |
Cặp khoá công khai và khoá riêng tư SSH. |
CREDENTIAL_TYPE_TOTP |
"totp" |
Mã bí mật của mật khẩu một lần dựa trên thời gian (2FA). |
CREDENTIAL_TYPE_WIFI |
"wifi" |
SSID và cụm mật khẩu của mạng Wi-Fi. |
Trình so khớp WASM (WebAssembly) tuỳ chỉnh (Nâng cao)
Theo mặc định, việc gọi RegisterExportRequest.create(context, entries) sẽ liên kết credential_transfer_matcher.wasm mặc định của hệ thống từ các thành phần thư viện, chỉ lọc các mục dựa trên giao điểm của supportedCredentialTypes.
Nếu trình cung cấp thông tin đăng nhập của bạn yêu cầu logic so khớp phức tạp (ví dụ: kiểm tra khả năng động hoặc lọc có điều kiện dựa trên các trường tuỳ chỉnh), bạn có thể biên dịch mô-đun WebAssembly của riêng mình khớp với API chuyển thông tin đăng nhập WASM và truyền trực tiếp mảng byte thô:
// Loading a custom WASM matcher val customMatcherBytes = context.assets.open("my_custom_matcher.wasm").use { it.readBytes() } val customRequest = RegisterExportRequest( entries = myEntries, exportMatcher = customMatcherBytes ) providerEventsManager.registerExport(customRequest)