Credential Manager के Credentials Transfer API की मदद से, क्रेडेंशियल उपलब्ध कराने वाली सेवाओं के बीच उपयोगकर्ता के क्रेडेंशियल को सुरक्षित तरीके से ट्रांसफ़र किया जा सकता है. हालांकि, इसके लिए ज़रूरी है कि दोनों सेवाएं एक ही डिवाइस पर मौजूद हों. इस गाइड में बताया गया है कि Android पर क्रेडेंशियल उपलब्ध कराने वाली सेवाएं, androidx.credentials:providerevents लाइब्रेरी से मिले एपीआई के साथ कैसे इंटिग्रेट कर सकती हैं. यह सुविधा, पासवर्ड, पासकी, पते की जानकारी, और कस्टम फ़ील्ड के साथ काम करती है. इसके लिए, FIDO क्रेडेंशियल एक्सचेंज फ़ॉर्मैट (सीएक्सएफ़) का इस्तेमाल किया जाता है.
सबसे ज़रूरी सिद्धांत
क्रेडेंशियल ट्रांसफ़र फ़्रेमवर्क की मदद से, एक ही डिवाइस पर पीयर-टू-पीयर क्रेडेंशियल ट्रांसफ़र किए जा सकते हैं. इसके लिए, Android ओएस या बिना पुष्टि वाले ऐप्लिकेशन को रॉ क्रेडेंशियल दिखाने की ज़रूरत नहीं पड़ती.
इस फ़्रेमवर्क में दो मुख्य भूमिकाएं तय की गई हैं:
- एक्सपोर्टर (सोर्स प्रोवाइडर): क्रेडेंशियल उपलब्ध कराने वाला ऐसा ऐप्लिकेशन जिसके पास फ़िलहाल उपयोगकर्ता के क्रेडेंशियल मौजूद हैं. यह सिस्टम में, एक्सपोर्ट किए जा सकने वाले खातों (
ExportEntry) के मेटाडेटा को पहले से रजिस्टर करता है. साथ ही, उपयोगकर्ता के चुनने पर, डेटा ट्रांसफ़र करने के अनुरोधों का जवाब देता है. - इंपोर्टर (क्रेडेंशियल प्रोवाइडर या सेटअप विज़र्ड): क्रेडेंशियल प्रोवाइडर या सेटअप विज़र्ड, जो इंपोर्ट करने का अनुरोध (
ImportCredentialsRequest) शुरू करता है. साथ ही, यह बताता है कि उसे किस तरह के क्रेडेंशियल और एक्सटेंशन मिल सकते हैं.
Android वर्शन के साथ काम करने की सुविधा
Credentials Transfer API, Android 8 (एपीआई लेवल 26) और इसके बाद के वर्शन वाले डिवाइसों पर काम करता है.
डिपेंडेंसी जोड़ें
अपने मॉड्यूल की build.gradle या build.gradle.kts फ़ाइल में, androidx.credentials:providerevents डिपेंडेंसी जोड़ें:
dependencies {
implementation("androidx.credentials:providerevents:1.0.0-alpha06")
}
ज़रूरी क्लास को इंस्टैंशिएट करना
ज़रूरी ProviderEventsManager का इंस्टेंस बनाएं.
val providerEventsManager = ProviderEventsManager.create(context)
एक्सपोर्टर को लागू करना
उपयोगकर्ताओं को आपके ऐप्लिकेशन से, डिवाइस पर मौजूद क्रेडेंशियल सेवा देने वाली अन्य कंपनियों के ऐप्लिकेशन में क्रेडेंशियल एक्सपोर्ट करने की अनुमति देने के लिए, एक्सपोर्टर की भूमिका लागू करें.
एक्सपोर्ट एंट्री रजिस्टर करना
जब क्रेडेंशियल की सेवा देने वाली कंपनी में बदलाव होता है (उदाहरण के लिए, कोई उपयोगकर्ता साइन इन करता है, क्रेडेंशियल जोड़ता है या खातों में बदलाव करता है), तो ProviderEventsManager.registerExport() का इस्तेमाल करके अपने ExportEntry आइटम रजिस्टर करें या अपडेट करें.
हर ExportEntry के लिए ज़रूरी है:
id: बिना किसी क्रम के जनरेट की गई एक सीक्रेट स्ट्रिंग आइडेंटिफ़ायर, जो इस एक्सपोर्ट एंट्री को यूनीक तरीके से दिखाती है. आपको इस आईडी को सुरक्षित रखना होगा. आपको बाद में, ट्रांसफ़र के लिए आने वाले अनुरोधों की पुष्टि करने के लिए इसकी ज़रूरत पड़ेगी.accountDisplayName: खाता लेबल (उदाहरण के लिए,"Personal Account"). यह ज़रूरी नहीं है.userDisplayName: उपयोगकर्ता का प्राइमरी आइडेंटिफ़ायर (उदाहरण के लिए,"alice@example.com").icon: सेवा देने वाली कंपनी या खाते को दिखाने वालाBitmapआइकॉन. लाइब्रेरी, इसे अपने-आप 32x32 PNG में बदल देती है.supportedCredentialTypes:CredentialTypesसे स्ट्रिंग कॉन्स्टेंट का एक सेट, जो यह दिखाता है कि इस एंट्री में किस तरह के टाइप शामिल हैं.
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) } }
मेनिफ़ेस्ट फ़ाइल में एक्सपोर्टर गतिविधि के बारे में बताएं
जब उपयोगकर्ता सिस्टम सिलेक्टर यूज़र इंटरफ़ेस (यूआई) पर आपके ExportEntry को चुनता है, तो सिस्टम आपके तय किए गए हैंडलिंग Activity को लॉन्च करता है. इस गतिविधि के बारे में, ज़रूरी इंटेंट ऐक्शन और कॉन्टेंट यूआरआई स्कीम के साथ एलान करें.
बिना अनुमति के ऐप्लिकेशन को शुरू करने और कन्फ़्यूज़्ड-डेप्युटी अटैक से बचने के लिए, Android 14 (एपीआई लेवल 34) और इसके बाद के वर्शन पर android:requireContentUriPermissionFromCaller="write" का एलान करें. इससे Android ऑपरेटिंग सिस्टम को यह पुष्टि करने का निर्देश मिलता है कि गतिविधि शुरू करने की अनुमति देने से पहले, लॉन्च करने वाले कॉलर के पास डेस्टिनेशन कॉन्टेंट यूआरआई में फ़ाइल या एंट्री में बदलाव करने की अनुमति है. जैसा कि इस उदाहरण में दिखाया गया है:
<activity
android:name="com.example.CredentialExportActivity"
android:exported="true"
android:requireContentUriPermissionFromCaller="write"
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>
अपनी गतिविधि में ट्रांसफ़र इंटेंट को मैनेज करना
अपने CredentialExportActivity में, अनुरोध को पार्स करने, कॉल करने वाले कॉम्पोनेंट और अनुरोध के मेटाडेटा की पुष्टि करने, ज़रूरी बायोमेट्रिक ऑथेंटिकेशन करने, और FIDO CXF पेलोड को दिए गए कॉन्टेंट यूआरआई में वापस लिखने के लिए, IntentHandler.retrieveProviderImportCredentialsRequest(intent) का इस्तेमाल करें.
अनुरोध की पुष्टि करना
आपकी गतिविधि एक्सपोर्ट की जाती है. इसलिए, क्रेडेंशियल एक्सपोर्ट करने के फ़्लो को सुरक्षित रखने के लिए, यहां दी गई हर लेयर की पुष्टि करें:
credIdसेशन नॉन्स की पुष्टि करें (मुख्य सुरक्षा): अपनेExportEntryको रजिस्टर करते समय, क्रिप्टोग्राफ़िक तरीके से रैंडम, हाई-एंट्रॉपीid(नॉन्स) जनरेट करें और उसे कम समय वाले सेशन से जोड़ें. अपनी गतिविधि में, पुष्टि करें किrequest.credId, सेवा देने वाली कंपनी की ओर से पहले जारी किए गए, चालू और समयसीमा खत्म न होने वाले सेशन नॉन्स से मेल खाता हो. तीसरे पक्ष के ऐप्लिकेशन इस नॉन्स का अनुमान नहीं लगा सकते. इससे यह पक्का होता है कि इंटेंट, सिस्टम सिलेक्टर पर उपयोगकर्ता के चुने गए विकल्प से मेल खाता है.Activity.getCallingPackage()की पुष्टि करना (कॉलर की जांच): क्रेडेंशियल सिलेक्टर,startActivityForResultका इस्तेमाल करके आपकी गतिविधि को लॉन्च करता है. इसलिए, आपकी गतिविधिgetCallingPackage()की जांच कर सकती है. इससे यह पुष्टि की जा सकती है कि यूज़र इंटरफ़ेस (यूआई) को लॉन्च करने वाला कॉलर, भरोसेमंद सिस्टम क्रेडेंशियल सिलेक्टर फ़्रेमवर्क है. Google Play services वाले Android डिवाइसों पर, यह"com.google.android.gms"है. अगर कॉल करने वाला पैकेज पहचाना नहीं जा सकता, तो अनुरोध अस्वीकार कर दें.request.callingAppInfo(टारगेट ऐप्लिकेशन की जानकारी) की जांच करें:callingAppInfoसे उस डेस्टिनेशन ऐप्लिकेशन की पहचान होती है जिसमें इंपोर्ट किए गए क्रेडेंशियल सेव किए जाएंगे. इस जानकारी का इस्तेमाल, पारदर्शिता के लिए अपने हिसाब से किया जा सकता है. जैसे, पुष्टि करने वाले यूज़र इंटरफ़ेस (यूआई) में, डेस्टिनेशन ऐप्लिकेशन का नाम और आइकॉन दिखाना. इसके अलावा, यह पुष्टि करने के लिए भी इस जानकारी का इस्तेमाल किया जा सकता है कि डेस्टिनेशन ऐप्लिकेशन भरोसेमंद है या नहीं.
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() } }
रजिस्ट्रेशन और क्लियरिंग से जुड़ी छूट
इस मॉड्यूल में मौजूद सभी अपवाद, ImportCredentialsException, RegisterExportException या ClearExportException की सबक्लास हैं.
RegisterExportProviderConfigurationExceptionयाClearExportProviderConfigurationException: यह गड़बड़ी तब होती है, जब सेवा देने वाली कंपनी के सेटअप से जुड़ी समस्याओं की वजह से, रजिस्ट्रेशन या डेटा मिटाने की प्रोसेस पूरी नहीं हो पाती. उदाहरण के लिए,ExportEntryमेंsupportedCredentialTypesखाली होना या ऐसे ओएस टियर पर कॉल करना जो काम नहीं करता.RegisterExportUnknownErrorExceptionयाClearExportUnknownErrorException: एक्सपोर्ट रजिस्ट्री को अपडेट करते समय, सिस्टम या स्टोरेज से जुड़ी कोई गड़बड़ी हुई. हालांकि, इस गड़बड़ी को किसी कैटगरी में नहीं रखा गया है.
इंपोर्टर को लागू करना
अपने ऐप्लिकेशन में क्रेडेंशियल इंपोर्ट करने के लिए (उदाहरण के लिए, ऑनबोर्डिंग या सेवा देने वाली कंपनी से इंपोर्ट करने के दौरान), इंपोर्टर की भूमिका लागू करें. इसके बाद, ProviderEventsManager.importCredentials() को कॉल करके प्रोसेस शुरू करें.
इंपोर्ट करने का अनुरोध और लॉन्च फ़्लो बनाना
यह बताएं कि आपका इंपोर्टर, किस CredentialTypes और KnownExtensions के साथ काम करता है:
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) {}
इंपोर्ट फ़्लो से जुड़ी गड़बड़ियां
इस मॉड्यूल में मौजूद सभी अपवाद, ImportCredentialsException, RegisterExportException या ClearExportException की सबक्लास हैं.
ImportCredentialsCancellationException: उपयोगकर्ता ने सिलेक्टर यूज़र इंटरफ़ेस (यूआई) को खारिज कर दिया या एक्सपोर्ट करने की गतिविधि (Activity.RESULT_CANCELED) को रद्द कर दिया.ImportCredentialsNoExportOptionException: रजिस्टर की गई कोई भी एक्सपोर्ट एंट्री, अनुरोध किए गए क्रेडेंशियल टाइप से मैच नहीं हुई या चुने गए एक्सपोर्टर ने अस्वीकार करने का अपवाद दिखाया.ImportCredentialsProviderConfigurationException: कॉन्फ़िगरेशन से जुड़ी गड़बड़ी (उदाहरण के लिए,ImportCredentialsRequestमें खालीcredentialTypesसेट किया गया है या अनुमतियां मौजूद नहीं हैं).ImportCredentialsInvalidJsonException: एक्सपोर्टर ने गलत तरीके से बनाया गया या खाली JSON पेलोड दिखाया. इस वजह से, अनुरोध की पुष्टि नहीं हो सकी.ImportCredentialsSystemErrorException: इंपोर्ट के दौरान, Android सिस्टम में कोई गड़बड़ी हुई या बाइंडर ट्रांसफ़र में कोई गड़बड़ी हुई.ImportCredentialsUnknownCallerException: फ़्रेमवर्क, कॉलिंग ऐप्लिकेशन की पुष्टि नहीं कर सका.ImportCredentialsUnknownErrorException: इंपोर्ट करने के दौरान, ऐसी गड़बड़ी हुई जिसे कैटगरी में नहीं रखा गया है या जिसकी उम्मीद नहीं थी.
इस्तेमाल किए जा सकने वाले क्रेडेंशियल टाइप और एक्सटेंशन
androidx.credentials.providerevents.transfer.CredentialTypes ऑब्जेक्ट, FIDO CXF आइटम टाइप के मुताबिक स्टैंडर्ड स्ट्रिंग कॉन्सटेंट तय करता है:
| कॉन्स्टेंट | वैल्यू (cxf टाइप) |
ब्यौरा |
|---|---|---|
CREDENTIAL_TYPE_BASIC_AUTH |
"basic-auth" |
उपयोगकर्ता नाम और पासवर्ड वाले लॉगिन क्रेडेंशियल. |
CREDENTIAL_TYPE_PUBLIC_KEY |
"passkey" |
FIDO2 या WebAuthn पासकी के सार्वजनिक पासकोड के क्रेडेंशियल. |
CREDENTIAL_TYPE_ADDRESS |
"address" |
फ़ॉर्म में जानकारी अपने-आप भरने के लिए, डाक या शिपिंग पते की जानकारी. |
CREDENTIAL_TYPE_API_KEY |
"api-key" |
एपीआई ऐक्सेस पासकोड और टोकन. |
CREDENTIAL_TYPE_CREDIT_CARD |
"credit-card" |
क्रेडिट और डेबिट कार्ड से पेमेंट करने की जानकारी. |
CREDENTIAL_TYPE_CUSTOM_FIELDS |
"custom-fields" |
कस्टम ग्रुप या उपयोगकर्ता के तय किए गए फ़ील्ड. |
CREDENTIAL_TYPE_DRIVERS_LICENSE |
"drivers-license" |
ड्राइविंग लाइसेंस की जानकारी. |
CREDENTIAL_TYPE_FILE |
"file" |
बाइनरी फ़ाइलों के लिए मेटाडेटा और प्लेसहोल्डर रेफ़रंस. |
CREDENTIAL_TYPE_GENERATED_PASSWORD |
"generated-password" |
मशीन से जनरेट किए गए सुरक्षित पासवर्ड. |
CREDENTIAL_TYPE_IDENTITY_DOCUMENT |
"identity-document" |
राष्ट्रीय आईडी कार्ड, एसएसएन, टीआईएन या पासपोर्ट रेफ़रंस. |
CREDENTIAL_TYPE_ITEM_REFERENCE |
"item-reference" |
पेलोड में मौजूद किसी अन्य आइटम पर ले जाने वाले लॉजिकल लिंक. |
CREDENTIAL_TYPE_NOTE |
"note" |
उपयोगकर्ता के तय किए गए सुरक्षित नोट (UTF-8 स्ट्रिंग). |
CREDENTIAL_TYPE_PASSPORT |
"passport" |
पासपोर्ट या यात्रा से जुड़े दस्तावेज़ की जानकारी. |
CREDENTIAL_TYPE_PERSON_NAME |
"person-name" |
व्यक्ति की पहचान और नाम की जानकारी. |
CREDENTIAL_TYPE_SSH_KEY |
"ssh-key" |
एसएसएच सार्वजनिक और निजी कुंजी के जोड़े. |
CREDENTIAL_TYPE_TOTP |
"totp" |
समय के हिसाब से एक बार इस्तेमाल होने वाले पासवर्ड (दो तरीकों से पुष्टि) के सीक्रेट. |
CREDENTIAL_TYPE_WIFI |
"wifi" |
वाई-फ़ाई नेटवर्क का SSID और लंबे पासवर्ड. |
कस्टम WASM (WebAssembly) मैचर (ऐडवांस)
डिफ़ॉल्ट रूप से, RegisterExportRequest.create(context, entries) को कॉल करने पर, लाइब्रेरी ऐसेट से सिस्टम-डिफ़ॉल्ट credential_transfer_matcher.wasm बंडल हो जाता है. इससे एंट्री को पूरी तरह से supportedCredentialTypes के इंटरसेक्शन के आधार पर फ़िल्टर किया जाता है.
अगर क्रेडेंशियल की सेवा देने वाली कंपनी को मैच करने के लिए मुश्किल लॉजिक की ज़रूरत है, तो WASM क्रेडेंशियल ट्रांसफ़र एपीआई से मैच करने वाले अपने WebAssembly मॉड्यूल को कंपाइल किया जा सकता है. साथ ही, रॉ बाइट कलेक्शन को सीधे पास किया जा सकता है. उदाहरण के लिए, डाइनैमिक क्षमता की जांच या कस्टम फ़ील्ड के आधार पर कंडीशनल फ़िल्टर करना:
// 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)