আপনার হোল্ডার অ্যাপের মাধ্যমে ক্রেডেনশিয়াল দেখান

Credential Manager Holder API আপনার Android হোল্ডার (যাকে "ওয়ালেট"ও বলা হয়) অ্যাপকে যাচাইকারীদের কাছে ডিজিটাল ক্রেডেনশিয়াল ম্যানেজ ও উপস্থাপন করতে দেয়। এই নির্দেশিকায় বর্ণনা করা হয়েছে যে কীভাবে ক্রেডেনশিয়াল ম্যানেজারের সাথে আপনার হোল্ডার অ্যাপের ক্রেডেনশিয়াল রেজিস্টার করবেন এবং ব্যবহারকারী কোনও ক্রেডেনশিয়াল বেছে নিলে কীভাবে প্রেজেন্টেশন অনুরোধ ম্যানেজ করবেন। Holder API, Android 6 (API লেভেল ২৩) এবং তার পরবর্তী যেকোনও ভার্সনে কাজ করে।

ডিপেন্ডেন্সি যোগ করুন

Credential Manager Holder API ব্যবহার করতে, আপনার অ্যাপ মডিউলের build.gradle স্ক্রিপ্টে নিম্নলিখিত ডিপেন্ডেন্সি যোগ করুন:

dependencies {
    // Used to implement credential registries
    implementation "androidx.credentials.registry:registry-digitalcredentials-mdoc:1.0.0-alpha05"
    implementation "androidx.credentials.registry:registry-digitalcredentials-openid:1.0.0-alpha05"
    implementation "androidx.credentials.registry:registry-digitalcredentials-sdjwtvc:1.0.0-alpha05"
    implementation "androidx.credentials.registry:registry-provider:1.0.0-alpha05"
    implementation "androidx.credentials.registry:registry-provider-play-services:1.0.0-alpha05"
}

আপনার ক্রেডেনশিয়াল রেজিস্টার করুন

প্রথমে, একটি RegistryManager ইনস্ট্যান্স তৈরি করুন যা আপনি একটি OpenId4VpRegistry অনুরোধ রেজিস্টার করতে ব্যবহার করবেন।

// Create the registry manager
val registryManager = RegistryManager.create(context)

OpenId4VpRegistry-এর অনুরোধ তৈরি করুন

যাচাইকারীর থেকে OpenID4VP অনুরোধ ম্যানেজ করতে আপনাকে OpenId4VpRegistry রেজিস্টার করতে হবে। আপনার ওয়ালেট ক্রেডেনশিয়ালের (যেমন, sdJwtsFromStorage এবং mdocsFromStorage) সাথে লোকাল ডেটা টাইপ লোড করা আছে ধরে নিয়ে, আপনাকে সেগুলি Jetpack DigitalCredentialEntry-এর সমতুল্য ডেটা টাইপে কনভার্ট করতে হবে: SdJwtEntry এবং MdocEntry।

রেজিস্ট্রিতে Sd-JWT যোগ করুন

রেজিস্ট্রির জন্য প্রতিটি লোকাল SD-JWT ক্রেডেনশিয়ালকে একটি SdJwtEntry-এ ম্যাপ করুন:

fun mapToSdJwtEntries(sdJwtsFromStorage: List<StoredSdJwtEntry>): List<SdJwtEntry> {
    val list = mutableListOf<SdJwtEntry>()

    for (sdJwt in sdJwtsFromStorage) {
        list.add(
            SdJwtEntry(
                verifiableCredentialType = sdJwt.getVCT(),
                claims = sdJwt.getClaimsList(),
                entryDisplayPropertySet = sdJwt.toDisplayProperties(),
                id = sdJwt.getId() // Make sure this cannot be readily guessed
            )
        )
    }
    return list
}

রেজিস্ট্রিতে mdocs যোগ করুন

আপনার স্থানীয় mdoc ক্রেডেনশিয়াল Jetpack টাইপে ম্যাপ করুন MdocEntry:

fun mapToMdocEntries(mdocsFromStorage: List<StoredMdocEntry>): List<MdocEntry> {
    val list = mutableListOf<MdocEntry>()

    for (mdoc in mdocsFromStorage) {
        list.add(
            MdocEntry(
                docType = mdoc.retrieveDocType(),
                fields = mdoc.getFields(),
                entryDisplayPropertySet = mdoc.toDisplayProperties(),
                id = mdoc.getId() // Make sure this cannot be readily guessed
            )
        )
    }
    return list
}

কোড সম্পর্কে গুরুত্বপূর্ণ পয়েন্ট

  • এনক্রিপ্ট করা আইডি: ID ফিল্ড কনফিগার করার একটি পদ্ধতি হল এনক্রিপ্ট করা ক্রেডেনশিয়াল আইডেন্টিফায়ার রেজিস্টার করা, যাতে শুধু আপনিই ভ্যালু ডিক্রিপ্ট করতে পারেন।
  • লোকালাইজেশন: দুটি ফর্ম্যাটের জন্যই UI ডিসপ্লে ফিল্ড লোকালাইজ করা উচিত।

আপনার অনুরোধ একত্রিত ও রেজিস্টার করুন

আপনার কনভার্ট করা এন্ট্রি একত্রিত করুন এবং এর সাথে অনুরোধ রেজিস্টার করুন RegistryManager:

val credentialEntries =
    mapToSdJwtEntries(sdJwtsFromStorage) + mapToMdocEntries(mdocsFromStorage)

val openidRegistryRequest = OpenId4VpRegistry(
    credentialEntries = credentialEntries,
    id = "my-wallet-openid-registry-v1" // A stable, unique ID to identify your registry record.
)

ঐচ্ছিক: ইনলাইন ইস্যু করা

ব্যবহারকারীর জন্য অনুরোধ করা ধরনের কোনও ক্রেডেনশিয়াল উপলভ্য না থাকলে, আপনি হোল্ডারে ক্রেডেনশিয়াল যোগ করার বিকল্প দেখাতে পারেন। এটি করতে, আপনার OpenId4VpRegistry-এ কাজ করে এমন ক্রেডেনশিয়াল সহ একটি inlineIssuanceEntries তালিকা যোগ করুন:

val openidRegistryRequest = OpenId4VpRegistry(
    credentialEntries = credentialEntries,
    inlineIssuanceEntries = emptyList(),
                listOf(
                MdocInlineIssuanceEntry(
                    id = "Issuance",
                    display = InlineIssuanceEntry.InlineIssuanceDisplayProperties(
                        subtitle = "Mobile Drivers License, State Id, and Others",
                    ),
                    supportedMdocs = setOf(
                        MdocInlineIssuanceEntry.SupportedMdoc(
                        "eu.europa.ec.eudi.pid.1"
                        ),
                        MdocInlineIssuanceEntry.SupportedMdoc(
                        "org.iso.18013.5.1.mDL1"
                        ),
                    )
                ),
                SdJwtInlineIssuanceEntry(
                    id = "sd-jwt-issuance",
                    display = InlineIssuanceEntry.InlineIssuanceDisplayProperties(
                        subtitle = "Mobile Drivers License, State Id, and Others",
                    ),
                    supportedSdJwts = setOf(
                        SdJwtInlineIssuanceEntry.SupportedSdJwt("urn:openid:interop:id:1")
                    )
                )
            ),
    id = "my-wallet-openid-registry-v1"
)

CredentialManager-এর মাধ্যমে আপনার ক্রেডেনশিয়াল রেজিস্টার করুন।

try {
    val response = registryManager.registerCredentials(openidRegistryRequest)
} catch (e: Exception) {
    // Handle failure
}

অ্যাপ মেটাডেটা ম্যানেজমেন্ট

CredentialManager-এর সাথে আপনার হোল্ডার অ্যাপ যে মেটাডেটা রেজিস্টার করে, তার নিম্নলিখিত প্রপার্টি আছে:

  • পারসিস্টেন্স: তথ্য স্থানীয়ভাবে সেভ করা হয়, রিবুট করার পরেও থেকে যায়।
  • সাইলো করা স্টোরেজ: প্রতিটি অ্যাপের রেজিস্ট্রি রেকর্ড আলাদাভাবে স্টোর করা হয়, অর্থাৎ একটি অ্যাপ অন্য অ্যাপের রেজিস্ট্রি রেকর্ড পরিবর্তন করতে পারে না।
  • কীড আপডেট: প্রতিটি অ্যাপের রেজিস্ট্রি রেকর্ড id-এর মাধ্যমে কীড করা হয়, যা রেকর্ড আবার শনাক্ত, আপডেট বা মুছে দেওয়ার অনুমতি দেয়।
  • মেটাডেটা আপডেট করা: আপনার অ্যাপ পরিবর্তন হলে বা প্রথমবার লোড হলে সেভ করা মেটাডেটা আপডেট করা ভাল অভ্যাস। একই id-এর অধীনে একাধিকবার রেজিস্ট্রি কল করা হলে, লেটেস্ট কল আগের সব রেকর্ড ওভাররাইট করে দেয়। আপডেট করতে, আগের রেকর্ড মুছে না দিয়েই আবার রেজিস্টার করুন।

ঐচ্ছিক: একটি ম্যাচিং টুল তৈরি করুন

ম্যাচার হল একটি Wasm বাইনারি যা ক্রেডেনশিয়াল ম্যানেজার একটি স্যান্ডবক্সে চালায় যাতে ইনকামিং যাচাইকারীর অনুরোধের সাথে আপনার রেজিস্টার করা ক্রেডেনশিয়াল ফিল্টার করা যায়।

  • ডিফল্ট ম্যাচিং: আপনি OpenId4VpRegistry ক্লাস ইনস্ট্যান্টিয়েট করলে, সেটি অটোমেটিক ডিফল্ট OpenId4VP ম্যাচিং (OpenId4VpDefaults.DEFAULT_MATCHER) অন্তর্ভুক্ত করে। সব স্ট্যান্ডার্ড OpenID4VP ব্যবহারের ক্ষেত্রে, লাইব্রেরি আপনার জন্য ম্যাচিং ম্যানেজ করে।
  • কাস্টম ম্যাচিং: আপনি শুধুমাত্র তখনই কাস্টম ম্যাচিং প্রয়োগ করবেন যখন আপনি এমন একটি নন-স্ট্যান্ডার্ড প্রোটোকলকে সমর্থন করছেন যার নিজস্ব ম্যাচিং লজিক প্রয়োজন।

বেছে নেওয়া ক্রেডেনশিয়াল ম্যানেজ করুন

ব্যবহারকারী কোনও ক্রেডেনশিয়াল বেছে নিলে, আপনার হোল্ডার অ্যাপকে অনুরোধটি ম্যানেজ করতে হবে। আপনাকে এমন একটি অ্যাক্টিভিটি নির্ধারণ করতে হবে যা androidx.credentials.registry.provider.action.GET_CREDENTIAL ইনটেন্ট ফিল্টার শোনে। আমাদের স্যাম্পেল ওয়ালেট এই পদ্ধতি দেখায়।

ইনটেন্ট, যাচাইকারীর অনুরোধ ও কলিং অরিজিনের মাধ্যমে আপনার অ্যাক্টিভিটি লঞ্চ করে, যা আপনি PendingIntentHandler.retrieveProviderGetCredentialRequest ফাংশন ব্যবহার করে এক্সট্র্যাক্ট করেন। এটি একটি ProviderGetCredentialRequest ফেরত দেয় যাতে যাচাইকারীর অনুরোধের সাথে সম্পর্কিত সব তথ্য থাকে। তিনটি মূল উপাদান আছে:

  • কলিং অ্যাপ: যে অ্যাপ অনুরোধ করেছে, সেটি getCallingAppInfo-এর মাধ্যমে ফিরিয়ে আনা যাবে। আপনি এটি ব্যবহার করে যাচাইকারীর পরিচয় চেক করতে পারবেন।
  • বেছে নেওয়া ক্রেডেনশিয়াল: ব্যবহারকারী কোন প্রার্থীকে বেছে নিয়েছেন সেই সংক্রান্ত তথ্য, যা selectedCredentialSet extension method-এর মাধ্যমে পাওয়া গেছে; এটি আপনার রেজিস্টার করা ক্রেডেনশিয়াল আইডির সাথে ম্যাচ করবে।
  • নির্দিষ্ট অনুরোধ: যাচাইকারীর করা নির্দিষ্ট অনুরোধ, getCredentialOptions পদ্ধতি থেকে উদ্ধার করা হয়েছে। ডিজিটাল ক্রেডেনশিয়াল অনুরোধের ফ্লোয়ের জন্য, আপনি এই তালিকায় একটি GetDigitalCredentialOption খুঁজে পাবেন বলে আশা করতে পারেন।

আপনি নিম্নলিখিত কোড ব্যবহার করে প্রেজেন্টেশনের অনুরোধ প্রসেস করতে পারবেন:

request.credentialOptions.forEach { option ->
    if (option is GetDigitalCredentialOption) {
        Log.i(TAG, "Got DC request: ${option.requestJson}")
        processRequest(option.requestJson)
    }
}

স্যাম্পেল ওয়ালেটে এর উদাহরণ দেখা যেতে পারে।

যাচাইকারীর পরিচয় চেক করুন

প্রথমে, ইনটেন্ট থেকে ProviderGetCredentialRequest এক্সট্র্যাক্ট করুন:

val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)

প্রিভিলেজড অ্যাপ চেক করুন: প্রিভিলেজড অ্যাপ (যেমন, ওয়েব ব্রাউজার) অরিজিন প্যারামিটার সেট করে অন্যান্য যাচাইকারীর হয়ে কল করতে পারে। এই অরিজিন পুনরুদ্ধার করতে, CallingAppInfo getOrigin API-তে বিশেষ সুবিধা প্রাপ্ত এবং বিশ্বস্ত কলারদের একটি তালিকা (JSON ফর্ম্যাটে একটি সাদাতালিকা) পাস করুন।

val origin = request?.callingAppInfo?.getOrigin(
    privilegedAppsJson // Your allow list JSON
)

origin খালি না থাকলে: packageName এবং সার্টিফিকেট ফিঙ্গারপ্রিন্ট আপনার অনুমোদিত তালিকায় থাকা কোনও অ্যাপের সাথে ম্যাচ করলে, অরিজিন ফিরিয়ে দেওয়া হয়। কলিং অ্যাপের স্বাক্ষর ব্যবহার করে উৎস গণনা করার পরিবর্তে, এটিকে একটি বিশেষাধিকারপ্রাপ্ত কল হিসেবে বিবেচনা করুন এবং OpenID4VP উত্তরে এই উৎস সেট করুন।

origin খালি থাকলে: যাচাইকারীর অনুরোধ Android অ্যাপ থেকে এসেছে। OpenID4VP উত্তরে ম্যাপ করা উৎস android:apk-key-hash:<encoded SHA 256 fingerprint> হিসেবে গণনা করা উচিত।

val appSigningInfo =
    request?.callingAppInfo?.signingInfoCompat?.signingCertificateHistory?.get(0)?.toByteArray()
val md = MessageDigest.getInstance("SHA-256")
val certHash =
    Base64.encodeToString(md.digest(appSigningInfo), Base64.NO_WRAP or Base64.NO_PADDING)
val computedOrigin = "android:apk-key-hash:$certHash"

হোল্ডার UI রেন্ডার করুন

কোনও ক্রেডেনশিয়াল বেছে নেওয়া হলে, হোল্ডার অ্যাপ চালু হয়ে যায় এবং অ্যাপের UI-এর মাধ্যমে ব্যবহারকারীকে নির্দেশনা দেয়। এই ওয়ার্কফ্লো ম্যানেজ করার দুটি সাধারণ পদ্ধতি রয়েছে:

  • ক্রেডেনশিয়াল রিলিজ করার জন্য ব্যবহারকারীর অতিরিক্ত যাচাইকরণের প্রয়োজন হলে, BiometricPrompt API ব্যবহার করুন। এটি স্যাম্পেলে দেখানো হয়েছে।
  • অন্যথায়, অনেক ওয়ালেট খালি অ্যাক্টিভিটি রেন্ডার করার মাধ্যমে সাইলেন্ট রিটার্ন বেছে নেয় যা কলিং অ্যাপে অবিলম্বে ডেটা ফেরত পাঠায়। এটি ব্যবহারকারীর ক্লিক করার প্রয়োজনীয়তা কমায় এবং আরও নির্বিঘ্ন অভিজ্ঞতা প্রদান করে।

ক্রেডেনশিয়াল সংক্রান্ত উত্তর ফেরত দিন

আপনার হোল্ডার অ্যাপ ফলাফল ফেরত পাঠানোর জন্য প্রস্তুত হয়ে গেলে, ক্রেডেনশিয়াল উত্তরের সাথে অ্যাক্টিভিটি শেষ করুন:

PendingIntentHandler.setGetCredentialResponse(
    resultData,
    GetCredentialResponse(DigitalCredential(response.responseJson))
)
setResult(RESULT_OK, resultData)
finish()

কোনও ব্যতিক্রম থাকলে, আপনি একইভাবে ক্রেডেনশিয়াল ব্যতিক্রম পাঠাতে পারেন:

PendingIntentHandler.setGetCredentialException(
    resultData,
    GetCredentialUnknownException() // Configure the proper exception
)
setResult(RESULT_OK, resultData)
finish()

প্রাসঙ্গিক ক্রেডেনশিয়াল রেসপন্স ফিরিয়ে দেওয়ার সম্পূর্ণ উদাহরণ দেখতে স্যাম্পেল অ্যাপ দেখুন।