رابط برنامهنویسی کاربردی مدیریت اعتبارنامه (Credential Manager Holder API) به برنامهی اندروید شما (که «کیف پول» نیز نامیده میشود) این امکان را میدهد که اعتبارنامههای دیجیتال را مدیریت و به تأییدکنندگان ارائه دهد.
مفاهیم اصلی
قبل از استفاده از Holder API، آشنایی با مفاهیم زیر ضروری است.
قالبهای اعتبارنامه
اعتبارنامهها میتوانند در برنامههای دارنده در قالبهای مختلف اعتبارنامه ذخیره شوند. این قالبها مشخصاتی برای نحوه نمایش یک اعتبارنامه هستند و هر یک شامل اطلاعات زیر در مورد اعتبارنامه است:
- نوع: دستهای مانند مدرک دانشگاهی یا گواهینامه رانندگی سیار.
- ویژگیها: ویژگیهایی مانند نام و نام خانوادگی.
- کدگذاری: نحوه ساختاردهی اعتبارنامه، برای مثال SD-JWT یا mdoc
- اعتبار: روشی برای تأیید رمزنگاری اصالت اعتبارنامه.
هر قالب اعتبارنامه، رمزگذاری و اعتبارسنجی را کمی متفاوت انجام میدهد، اما از نظر عملکردی یکسان هستند.
رجیستری از دو فرمت پشتیبانی میکند:
- SD-JWT: با مشخصات اعتبارنامههای قابل تأیید مبتنی بر SD-JWT (SD-JWT VC) از IETF مطابقت دارد.
- اسناد موبایل یا mdocs: مطابق با مشخصات ISO/IEC 18013-5:2021 است.
یک تأییدکننده ممکن است هنگام استفاده از Credential Manager درخواست OpenID4VP برای SD-JWT و mdocs ارسال کند. انتخاب بسته به مورد استفاده و انتخاب صنعت متفاوت است.
ثبت فراداده اعتبارنامه
Credential Manager مستقیماً اطلاعات مربوط به دارنده اعتبارنامه را ذخیره نمیکند، بلکه فرادادههای مربوط به آن را ذخیره میکند. یک برنامه دارنده اعتبارنامه ابتدا باید فرادادههای مربوط به دارنده اعتبارنامه را با استفاده RegistryManager در Credential Manager ثبت کند. این فرآیند ثبت، یک رکورد رجیستری ایجاد میکند که دو هدف کلیدی را دنبال میکند:
- تطبیق: از فرادادههای اعتبارنامه ثبتشده برای تطبیق با درخواستهای تأییدکننده آینده استفاده میشود.
- نمایش: عناصر رابط کاربری سفارشیشده در رابط انتخابگر اعتبارنامه به کاربر نشان داده میشوند.
شما از کلاس OpenId4VpRegistry برای ثبت اعتبارنامههای دیجیتال خود استفاده خواهید کرد، زیرا از هر دو قالب اعتبارنامه mdoc و SD-JWT پشتیبانی میکند. تأییدکنندگان درخواستهای OpenID4VP را برای درخواست این اعتبارنامهها ارسال میکنند.
اعتبارنامههای برنامه خود را ثبت کنید
برای استفاده از رابط برنامهنویسی کاربردی (API) مربوط به Credential Manager Holder، وابستگیهای زیر را به اسکریپت ساخت ماژول برنامه خود اضافه کنید:
شیار
dependencies { // Use to implement credentials registrys implementation "androidx.credentials.registry:registry-digitalcredentials-mdoc:1.0.0-alpha04" implementation "androidx.credentials.registry:registry-digitalcredentials-preview:1.0.0-alpha04" implementation "androidx.credentials.registry:registry-provider:1.0.0-alpha04" implementation "androidx.credentials.registry:registry-provider-play-services:1.0.0-alpha04" }
کاتلین
dependencies { // Use to implement credentials registrys implementation("androidx.credentials.registry:registry-digitalcredentials-mdoc:1.0.0-alpha04") implementation("androidx.credentials.registry:registry-digitalcredentials-preview:1.0.0-alpha04") implementation("androidx.credentials.registry:registry-provider:1.0.0-alpha04") implementation("androidx.credentials.registry:registry-provider-play-services:1.0.0-alpha04") }
ایجاد مدیر رجیستری
یک نمونه RegistryManager ایجاد کنید و یک درخواست OpenId4VpRegistry را با آن ثبت کنید.
// Create the registry manager
val registryManager = RegistryManager.create(context)
// The guide covers how to build this out later
val registryRequest = OpenId4VpRegistry(credentialEntries, id)
try {
registryManager.registerCredentials(registryRequest)
} catch (e: Exception) {
// Handle exceptions
}
ساخت یک درخواست OpenId4VpRegistry
همانطور که قبلاً ذکر شد، برای مدیریت درخواست OpenID4Vp از یک تأییدکننده، باید یک OpenId4VpRegistry ثبت کنید. فرض میکنیم که شما برخی از انواع دادههای محلی را با اعتبارنامههای کیف پول خود بارگذاری کردهاید (به عنوان مثال، sdJwtsFromStorage ). اکنون آنها را بر اساس قالبشان - SdJwtEntry یا MdocEntry به ترتیب برای SD-JWT یا mdoc - به معادلهای Jetpack DigitalCredentialEntry ما تبدیل خواهید کرد.
اضافه کردن 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، ثبت یک شناسهی اعتبارنامهی رمزگذاریشده است، به طوری که فقط شما بتوانید مقدار را رمزگشایی کنید. - فیلدهای نمایش رابط کاربری برای هر دو قالب باید بومیسازی شوند.
اعتبارنامههای خود را ثبت کنید
ورودیهای تبدیلشدهی خود را ترکیب کرده و درخواست را با 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.
)
اکنون، ما آمادهایم تا اعتبارنامههای شما را در CredentialManager ثبت کنیم.
try {
val response = registryManager.registerCredentials(openidRegistryRequest)
} catch (e: Exception) {
// Handle failure
}
اکنون اعتبارنامههای خود را در Credential Manager ثبت کردهاید.
مدیریت فرادادههای برنامه
فرادادهای که برنامهی دارنده شما در CredentialManager ثبت میکند، دارای ویژگیهای زیر است:
- ماندگاری: اطلاعات به صورت محلی ذخیره میشوند و در طول راهاندازی مجدد سیستم باقی میمانند.
- ذخیرهسازی سیلویی: رکوردهای رجیستری هر برنامه به طور جداگانه ذخیره میشوند، به این معنی که یک برنامه نمیتواند رکوردهای رجیستری برنامه دیگر را تغییر دهد.
- بهروزرسانیهای کلیددار: رکوردهای رجیستری هر برنامه توسط یک
id) کلیدگذاری میشوند که امکان شناسایی مجدد، بهروزرسانی یا حذف رکوردها را فراهم میکند. - بهروزرسانی متادیتا: بهتر است هر زمان که برنامه شما تغییر میکند یا برای اولین بار بارگذاری میشود، متادیتای پایدار را بهروزرسانی کنید. اگر یک رجیستری چندین بار تحت یک
idیکسان فراخوانی شود، آخرین فراخوانی تمام رکوردهای قبلی را بازنویسی میکند. برای بهروزرسانی، بدون نیاز به پاک کردن رکورد قدیمی، دوباره ثبتنام کنید.
اختیاری: ایجاد یک تطبیقدهنده
تطبیقدهنده (matcher) یک فایل باینری Wasm است که Credential Manager آن را در یک محیط سندباکس (sandbox) اجرا میکند تا اعتبارنامههای ثبتشده شما را در برابر درخواست Verifier ورودی فیلتر کند.
- تطبیقدهنده پیشفرض: کلاس
OpenId4VpRegistryبهطور خودکار تطبیقدهنده پیشفرضOpenId4VP(OpenId4VpDefaults.DEFAULT_MATCHER) را هنگام نمونهسازی آن، در خود جای میدهد. برای همه موارد استفاده استاندارد OpenID4VP، این کتابخانه تطبیق را برای شما انجام میدهد. - تطبیقدهنده سفارشی: شما فقط در صورتی از تطبیقدهنده سفارشی استفاده میکنید که از پروتکل غیراستانداردی پشتیبانی کنید که به منطق تطبیق خاص خود نیاز دارد.
مدیریت اعتبارنامه انتخاب شده
وقتی کاربری یک اعتبارنامه را انتخاب میکند، برنامهی دارنده شما باید درخواست را مدیریت کند. شما باید یک Activity تعریف کنید که به فیلتر intent مربوط به androidx.credentials.registry.provider.action.GET_CREDENTIAL گوش دهد. کیف پول نمونه ما این رویه را نشان میدهد .
این intent، activity شما را با درخواست Verifier و فراخوانی origin اجرا میکند که شما آن را با تابع PendingIntentHandler.retrieveProviderGetCredentialRequest استخراج میکنید . این تابع یک ProviderGetCredentialRequest برمیگرداند که شامل تمام اطلاعات مرتبط با درخواست verifier است. سه جزء کلیدی وجود دارد:
- برنامهی فراخوانی: برنامهای که درخواست را ارسال کرده است و با
getCallingAppInfoقابل بازیابی است. - اعتبارنامهی انتخابشده: اطلاعات مربوط به کاندیدایی که کاربر انتخاب کرده است، که از طریق
selectedCredentialSet extension methodبازیابی میشود؛ این با شناسهی اعتبارنامهای که ثبت کردهاید مطابقت خواهد داشت. - درخواستهای خاص: درخواست خاصی که توسط تأییدکننده ارسال شده و از متد
getCredentialOptionsبازیابی میشود. برای جریان درخواست Digital Credentials، میتوانید انتظار داشته باشید که یکGetDigitalCredentialOptionواحد را در این لیست پیدا کنید.
معمولاً، تأییدکننده درخواست ارائه اعتبارنامه دیجیتال را ارسال میکند که میتوانید با کد نمونه زیر آن را پردازش کنید:
request.credentialOptions.forEach { option ->
if (option is GetDigitalCredentialOption) {
Log.i(TAG, "Got DC request: ${option.requestJson}")
processRequest(option.requestJson)
}
}
نمونهای از این مورد را میتوان در کیف پول نمونه مشاهده کرد.
هویت تأییدکننده را بررسی کنید
- درخواست
ProviderGetCredentialRequestرا از intent استخراج کنید:
val request = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
- بررسی مبدأ ممتاز: برنامههای ممتاز (مانند مرورگرهای وب) میتوانند با تنظیم پارامتر origin از طرف سایر تأییدکنندگان، فراخوانی انجام دهند. برای بازیابی این مبدأ، باید لیستی از فراخوانیکنندگان ممتاز و قابل اعتماد (یک لیست مجاز در قالب JSON) را به API
getOrigin()درCallingAppInfoارسال کنید.
val origin = request?.callingAppInfo?.getOrigin(
privilegedAppsJson // Your allow list JSON
)
اگر origin خالی نباشد: origin برگردانده میشود اگر packageName و اثر انگشت گواهی بهدستآمده از signingInfo با اثر انگشتهای برنامهای که در لیست allow ارسالشده به getOrigin() API یافت میشود، مطابقت داشته باشند. پس از بهدست آوردن مقدار origin، برنامه ارائهدهنده باید این را یک فراخوانی ممتاز در نظر بگیرد و این origin را روی پاسخ OpenID4VP تنظیم کند، بهجای اینکه origin را با استفاده از امضای برنامه فراخوانیشده محاسبه کند.
مدیریت رمز عبور گوگل از یک لیست مجازِ در دسترس عموم برای فراخوانیهای getOrigin() استفاده میکند. به عنوان یک ارائهدهندهی اعتبارنامه، میتوانید از این لیست استفاده کنید یا لیست خودتان را در قالب JSON که توسط API شرح داده شده است، ارائه دهید. انتخاب لیست مورد استفاده بر عهدهی ارائهدهنده است. برای دسترسی ممتاز به ارائهدهندگان اعتبارنامهی شخص ثالث، به مستندات ارائه شده توسط شخص ثالث مراجعه کنید.
اگر origin خالی باشد، درخواست تأییدکننده از یک برنامه اندروید است. origin برنامهای که قرار است در پاسخ OpenID4VP قرار گیرد باید به صورت android:apk-key-hash:<encoded SHA 256 fingerprint> محاسبه شود.
val appSigningInfo = request?.callingAppInfo?.signingInfoCompat?.signingCertificateHistory[0]?.toByteArray()
val md = MessageDigest.getInstance("SHA-256")
val certHash = Base64.encodeToString(md.digest(appSigningInfo), Base64.NO_WRAP or Base64.NO_PADDING)
return "android:apk-key-hash:$certHash"
رابط کاربری Holder را رندر کنید
وقتی یک اعتبارنامه انتخاب میشود، برنامهی دارنده آن فراخوانی میشود و کاربر را در رابط کاربری برنامه راهنمایی میکند. دو روش استاندارد برای انجام این گردش کار وجود دارد:
- اگر برای آزادسازی اعتبارنامه به احراز هویت کاربر بیشتری نیاز باشد، از API BiometricPrompt استفاده کنید. این مورد در نمونه نشان داده شده است.
- در غیر این صورت، بسیاری از کیف پولها با ارائه یک فعالیت خالی که بلافاصله دادهها را به برنامه فراخوانی منتقل میکند، بازگشت بیصدا را انتخاب میکنند. این کار کلیکهای کاربر را به حداقل میرساند و تجربهای یکپارچهتر را ارائه میدهد.
پاسخ اعتبارنامه را برگردانید
زمانی که برنامهی دارنده شما آمادهی ارسال نتیجه شد، فعالیت را با پاسخ اعتبارنامه به پایان برسانید:
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()
برای مشاهدهی مثال کاملی از نحوهی بازگرداندن پاسخ اعتبارنامه در متن، به برنامهی نمونه مراجعه کنید.