این صفحه نحوه ایجاد، ورود به سیستم، و حذف کلید بازیابی را شرح میدهد.
مهارتهای Android
مشاهده در GitHubبازگرداندن اعتبارنامهها
android skills add restore-credentialsسازگاری نسخه
«بازگرداندن اعتبارنامهها» در «مدیر اعتبارنامه» در دستگاههای دارای Android 9 (سطح ایپیآی ۲۸) و بالاتر، نسخه اصلی 242200000 (v24.22) یا بالاتر «خدمات Google Play» (GMS)، و نسخه 1.5.0 یا بالاتر کتابخانه androidx.credentials کار میکند. در دستگاههای فاقد خدمات Google Play یا دارای نسخه قدیمی GMS،
CreateCredentialProviderConfigurationException و
GetCredentialProviderConfigurationException را بگیرید تا برنامهتان بهراحتی به ورود به سیستم استاندارد
برگردد.
پیشنیازها
سرور طرف متکی WebAuthn را مشابه سرور گذرکلیدها پیکربندی کنید. اگر ازقبل سروری برای رسیدگی به اصالتسنجی با گذرکلیدها راهاندازی کردهاید، میتوانید از همان نقاط پایانی ثبت و ادعای WebAuthn برای کلیدهای بازیابی استفاده کنید.
وابستگیها
وابستگیهای زیر را به فایل build.gradle واحد برنامهتان اضافه کنید:
کاتلین
dependencies { implementation("androidx.credentials:credentials:1.7.0-alpha03") implementation("androidx.credentials:credentials-play-services-auth:1.7.0-alpha03") }
شیار
dependencies { implementation "androidx.credentials:credentials:1.7.0-alpha03" implementation "androidx.credentials:credentials-play-services-auth:1.7.0-alpha03" }
«بازگرداندن اعتبارنامهها» از نسخه 1.5.0 و بالاتر کتابخانه androidx.credentials دردسترس است. بااینحال، توصیه میشود درصورت امکان از جدیدترین
نسخههای پایدار وابستگیها استفاده کنید.
نحوه تأثیر android:allowBackup بر «بازگرداندن اعتبارنامهها»
مشخصه مانیفست android:allowBackup پشتیبانگیری دادههای برنامه را کنترل میکند،
که به سه روش متمایز با «بازگرداندن اعتبارنامهها» تعامل دارد:
- انتقال کلید بازیابی (مستقل از
allowBackup): خدمات Google Play جفت کلیدRestoreCredentialرا بااستفاده از پشتیبانگیری ابری رمزگذاریشده سرتاسر یا انتقال محلی دستگاهبهدستگاه (D2D) منتقل میکند، صرفنظر از اینکهandroid:allowBackup،trueیاfalseباشد. برای انتقالهای دستگاه به دستگاه، کلید بازیابی همیشه منتقل میشود؛ تنها شرایطی که در آن منتقل نمیشود، درطول پشتیبانگیری ابری است، زمانی که کاربر در تنظیمات Android از پشتیبانگیری ابری انصراف داده باشد. - بازیابی پسزمینه در
BackupAgent(بهandroid:allowBackup="true"نیاز دارد): ورود به سیستم بیصدا در پسزمینه قبلاز اینکه کاربر برنامه را در دستگاه جدید باز کند (BackupAgent.onRestoreFinished()) فقط زمانی اجرا میشود کهandroid:allowBackup="true"وandroid:backupAgentدر مانیفست شما پیکربندی شده باشند. اگرandroid:allowBackup="false"، وقتی کاربر برای اولینبارActivityاصلی شما را راهاندازی میکند، جلسه را بازیابی کنید. همچنین میتوانید هر دو رویکرد را برای قویتر کردن بازیابی پیادهسازی کنید. - آزمایش «پشتیبانگیری ابری Android Studio» (به
android:allowBackup="true"نیاز دارد): آزمایش نوع پشتیبانگیری ابری در ابزار «پشتیبانگیری و بازیابی» Android Studio بهandroid:allowBackup="true"نیاز دارد، درحالیکه آزمایش دستگاه به دستگاه بدون توجه بهallowBackupکار میکند.
نمای کلی
«بازگرداندن اعتبارنامهها» از چرخه حیات سهمرحلهای در دستگاههای قبلی و جدید کاربر پیروی میکند:
- ایجاد کلید بازیابی در دستگاه قبلی: پساز اینکه کاربر به سیستم وارد شد (یا هنگام راهاندازی برنامه اگر کاربر قبلاً به سیستم وارد شده باشد و هیچ کلید بازیابیای در فضای ذخیرهسازی محلی ثبت نشده باشد)، گزینههای ایجاد را از سرور طرف معتمدتان واکشی کنید و
credentialManager.createCredential()را فراخوانی کنید. - ورود به سیستم با کلید بازیابی در دستگاه جدید:
RestoreCredentialمنتقلشده را بااستفاده از استراتژی دو سطحی بازیابی کنید: ابتدا بهصورت بیصدا درBackupAgent.onRestoreFinished()(وقتیandroid:allowBackup="true") تا همگامسازی پسزمینه و اعلانهای لحظهای فوراً ازسر گرفته شود، و دوم بهعنوان جایگزین در راهاندازActivity.onCreate()در اولین راهاندازی برنامه. - حذف کلید بازیابی هنگام خروج از سیستم: وقتی کاربر از سیستم خارج میشود یا
جلسه او توسط سرور نامعتبر میشود،
credentialManager.clearCredentialState()را باClearCredentialStateRequest(ClearCredentialStateRequest.TYPE_CLEAR_RESTORE_CREDENTIAL)فراخوانی کنید و پرچم همگامسازی محلی خود را پاک کنید.
ایجاد کلید بازیابی
برنامه شما باید همه موارد ورود به سیستم کاربر را پوشش دهد تا مطمئن شوید که کاربران فعال کلید بازیابی ایجاد کردهاند. در سناریوهای زیر کلید بازیابی ایجاد کنید:
- اگر کاربر به سیستم وارد شده باشد و کلید بازیابی ازقبل ایجاد نشده باشد (مثلاً در
روش
onCreate()برایActivityاصلی). - وقتی کاربر درحال ورود به سیستم یا تکمیل جریان ثبتنام حساب جدید است.
برای جلوگیری از فراخوانی createCredential() در هر راهاندازی برنامه، یک پرچم همگامسازی محلی مانند has_synced_restore_credential را در فضای ذخیرهسازی محلی ذخیره کنید. این پرچم را در SharedPreferences یا DataStore پشتیبانگیریشده پیشفرض ذخیره نکنید، مگراینکه آن فایل در data_extraction_rules.xml مستثنی شده باشد؛ درغیراینصورت، «پشتیبانگیری خودکار» has_synced_restore_credential = true را در دستگاه جدید کپی میکند و مانع از تولید یا چرخش کلید بازیابی دستگاه جدید میشود.
نمونهسازی Credential Manager
از بافت فعالیت برنامهتان برای نمونهسازی شیء CredentialManager استفاده کنید.
// Use your app or activity context to instantiate a client instance of
// CredentialManager.
private val credentialManager = CredentialManager.create(context)
دریافت گزینههای ایجاد اطلاعات اعتباری از سرور برنامه
از کتابخانه سازگار با FIDO در سرور برنامهتان استفاده کنید تا
JSON PublicKeyCredentialCreationOptions موردنیاز برای ایجاد اعتبارنامه بازیابی را به برنامه مشتریتان ارسال کنید. برای اطلاعات بیشتر درباره پیادهسازی سمت سرور،
راهنمای سمت سرور را ببینید.
گزینههای ایجاد JSON از همان ساختار ثبت گذرکلید پیروی میکند:
{
"challenge": "<base64url-encoded challenge>",
"rp": {
"name": "<relying party name>",
"id": "<relying party host name>"
},
"user": {
"id": "<base64url-encoded user ID>",
"name": "<user name>",
"displayName": "<user display name>"
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
}
],
"attestation": "none",
"excludeCredentials": [
{
"id": "<base64url-encoded credential ID to exclude>",
"type": "public-key"
}
],
"authenticatorSelection": {
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
مطمئن شوید JSON گزینههای ایجاد شما این الزامات را برآورده میکند:
challengeوuser.id: هر دو مقدار را بهعنوان رشتههای Base64URL بدون فاصله کدبندی کنید (+را با-،/را با_جایگزین کنید و=انتهایی را حذف کنید). نویسههای استاندارد Base64 یا شناسههای عدد صحیح خام باعثCreateRestoreCredentialDomExceptionیاIllegalArgumentExceptionمیشوند.rp.id: روی دامنه طرف معتمدتان تنظیم شده است (برای مثال،example.com).-
pubKeyCredParams: شامل{"type": "public-key", "alg": -7}(ES256) و{"type": "public-key", "alg": -257}(RS256).
ایجاد کلید بازیابی
پساز دریافت JSON گزینههای ایجاد کلید عمومی از سرور، JSON را در شیء CreateRestoreCredentialRequest قرار دهید و createCredential() را در شیء CredentialManager فراخوانی کنید.
// createRestoreRequest contains the details sent by the server
val response = credentialManager.createCredential(context, createRestoreRequest)
نکات کلیدی درباره کد
شیء
CreateRestoreCredentialRequestحاوی فیلدهای زیر است:-
requestJson: گزینههای ایجاد اطلاعات کاربری که توسط سرور برنامه در قالب Web Authentication API برایPublicKeyCredentialCreationOptionsJSONارسال شده است.
isCloudBackupEnabled: فیلدBooleanبرای تعیین اینکه آیا کلید بازیابی باید در فضای ابری پشتیبانگیری شود یا نه. بهطور پیشفرض، این پرچمtrueاست. این فیلد این مقادیر را دارد:true: (توصیهشده) این مقدار پشتیبانگیری از کلیدهای بازیابی را در فضای ابری فعال میکند، بهشرطی که کاربر «پشتیبانگیری Google» و رمزگذاری سرتاسر (مانند قفل صفحه) را فعال کرده باشد.false: این مقدار کلید را بهصورت محلی و نه در فضای ابری ذخیره میکند. اگر کاربر انتخاب کند که از فضای ابری بازیابی کند، کلید در دستگاه جدید دردسترس نخواهد بود، اما همچنان درطول راهاندازی محلی دستگاه به دستگاه (D2D) ازطریق کابل یا Wi-Fi منتقل میشود.
-
مدیریت پاسخ ایجاد اطلاعات اعتباری
Credential Manager API پاسخی از نوع
CreateRestoreCredentialResponse برمیگرداند. این پاسخ حاوی پاسخ ثبت اعتبارنامه کلید عمومی در قالب JSON است.
response.registrationResponseJson را از برنامهتان به سرور طرف اعتماد خود ارسال کنید تا کلید عمومی را در کنار حساب کاربر درستیسنجی و ذخیره کند، سپس نشانگر همگامسازی محلیتان را در فضای ذخیرهسازی محلی ثبت کنید. برای اطلاعات بیشتر درباره درستیسنجی سمت سرور، راهنمای گذرکلیدها را ببینید.
درطول فرایند ایجاد کلید بازیابی، این استثناها را مدیریت کنید:
CreateRestoreCredentialDomException: این استثنا زمانی رخ میدهد کهrequestJsonنامعتبر باشد و از قالب WebAuthn برایPublicKeyCredentialCreationOptionsJSONپیروی نکند.E2eeUnavailableException: این استثنا زمانی رخ میدهد کهisCloudBackupEnabledرویtrueباشد، اما دستگاه کاربر پشتیبانگیری داده یا رمزگذاری سرتاسر، مثل قفل صفحه، نداشته باشد.
برای اطمینان از اینکه «اعتبارنامههای بازگرداندن» برای انتقالهای محلی دستگاهبهدستگاه (D2D) ایجاد میشود، حتی وقتی «رمزگذاری سرتاسر» ابری دردسترس نیست،E2eeUnavailableExceptionرا بگیرید و بلافاصلهcreateCredential()را باCreateRestoreCredentialRequest(requestJson, isCloudBackupEnabled = false)دوباره امتحان کنید.IllegalArgumentException: این استثنا زمانی رخ میدهد کهrequestJsonخالی باشد، JSON معتبر نباشد، یاuser.idکدبندیشده Base64URL معتبری نداشته باشد که با مشخصات WebAuthn مطابقت داشته باشد.
ورود به سیستم با کلید بازیابی
از «بازگرداندن اعتبارنامهها» برای ورود بیصدا به سیستم کاربر درطول فرایند راهاندازی دستگاه استفاده کنید.
دریافت گزینههای بازیابی اطلاعات اعتباری از سرور برنامه
قبلاز فراخوانی getCredential() در دستگاه جدید، درخواست چالش JSON
PublicKeyCredentialRequestOptions WebAuthn را از سرور برنامهتان ارسال کنید. چون
برنامه در دستگاه جدید هنوز نمیداند کدام کاربر درحال ورود به سیستم است، این
نقطه پایانی باید تأییدنشده باشد (اکتشاف بدون کاربر). برای راهنماییهای مربوط به سرور، به ورود به سیستم با گذرکلید و راهنمای اصالتسنجی سمت سرور مراجعه کنید.
JSON گزینههای بازیابی ساختار زیر را دارد:
{
"challenge": "<your app challenge>",
"allowCredentials": [],
"rpId": "<your app server domain>"
}
گزینههای بازیابی JSON را در سرورتان بهصورت زیر پیکربندی کنید:
allowCredentials: این فیلد را حذف کنید یا آرایه خالی ([]) ارسال کنید چون حساب کاربر قبلاز بازیابی مشخص نیست. پساز بازگشتgetCredential()، سرور شما کاربر را از فیلدresponse.userHandle(که باuser.idثبتشده درطول ایجاد مطابقت دارد) شناسایی میکند و کلید عمومی را باid(credentialId) جستجو میکند.userVerification: روی"discouraged"(یا"preferred") تنظیم شده است. چون «بازگرداندن اعتبارنامهها» بدون واسط کاربر زیستسنجشی یا قفل صفحه در پسزمینه اجرا میشود، Android"required"را به"discouraged"لغو میکند و دادههای اصالتسنجی برگشتدادهشده به سرور شما پرچم «کاربر تأییدشده» (uv) را رویfalseتنظیم کرده است. تأییدکننده سرور «اصالتسنجی وب» خود را پیکربندی کنید تاuv = falseرا برای ادعاهای کلید بازیابی بپذیرد.
دریافت کلید بازیابی
برای دریافت کلید بازیابی در دستگاه جدید، requestJson سرور را در
GetRestoreCredentialOption داخل GetCredentialRequest بپیچید و
getCredential() را در شیء CredentialManager فراخوانی کنید.
راهبرد بازیابی دو سطحی را پیادهسازی کنید تا کاربران چه بازیابی دادههای برنامه پسزمینه اجرا شود یا نه، به سیستم وارد شوند:
- سطح ۱ (بازیابی پسزمینه در
BackupAgent.onRestoreFinished()): اگر پشتیبانگیری دادههای برنامه فعال است (android:allowBackup="true")، زیرکلاسBackupAgentو لغوonRestoreFinished()(ازonRestore()استفاده نکنید، که فقط برای پشتیبانگیریهای قدیمی مقدار کلید فراخوانی میشود). چونonRestoreFinished()همگام است وgetCredential()یک تابعsuspendKotlin است، تماس را بااستفاده ازrunBlocking(Dispatchers.IO)پل کنید. CatchNoCredentialExceptionquietly when no restore key is present. - سطح ۲ (جایگزین پیشزمینه در راهانداز
Activity.onCreate()): در اولین راهاندازی برنامه وقتی هیچ جلسه محلی وجود ندارد،getCredential()را ازlifecycleScopeقبلاز نمایش صفحه ورود به سیستم فراخوانی کنید. این کار برنامههای دارایandroid:allowBackup="false"را مدیریت میکند و اگر دستگاه هنگام اجرایonRestoreFinished()آفلاین بود، بازیابی میکند.
اگر BackupAgent جدیدی به برنامهای اضافه میکنید که ازقبل از «پشتیبانگیری خودکار» با
android:allowBackup="true" استفاده میکند، android:fullBackupOnly="true" را نیز در
عنصر <application> در AndroidManifest.xml تنظیم کنید تا Android همچنان از
«پشتیبانگیری خودکار» استفاده کند و به پشتیبانگیری قدیمی مقدار کلید تغییر نکند.
// Fetch the options required to get the restore key
val authenticationJson = fetchAuthenticationJson()
// Create the GetRestoreCredentialRequest object
val options = GetRestoreCredentialOption(authenticationJson)
val getRequest = GetCredentialRequest(listOf(options))
val response = credentialManager.getCredential(context, getRequest)
// Type-check and extract the restore credential
val credential = response.credential as RestoreCredential
Credential Manager API یک GetCredentialResponse برمیگرداند که
ویژگی credential آن یک RestoreCredential (نه یک
PublicKeyCredential) حاوی authenticationResponseJson است.
رسیدگی به پاسخ ورود به سیستم
restoreCredential.authenticationResponseJson را از برنامه به سرور طرف متکی خود ارسال کنید تا ادعای رمزنگاری را درستیسنجی کنید و کد جلسه جدیدی برای کاربر صادر کنید. برای جزئیات درستیسنجی سمت سرور، ورود به سیستم با گذرکلید را ببینید.
هنگام طراحی فضای ذخیرهسازی پسزمینه و خطمشیهای چرخه حیات برای کلیدهای بازیابی، این دستورالعملها را دنبال کنید:
- کلیدهای بازیابی را از گذرکلیدهای کاربر متمایز کنید: کلیدهای بازیابی را در پایگاه دادهتان برچسبگذاری کنید تا از صفحههای مدیریت گذرکلید سمت کاربر پنهان شوند و از بررسیهای درستیسنجی کاربر (
uv = true) معاف شوند. - پشتیبانی از چند دستگاه فعال برای هر کاربر: کاربر میتواند چند دستگاه (مثل تلفن و رایانه لوحی) داشته باشد. بهجای بازنویسی یک ردیف، چند کلید بازیابی فعال برای هر حساب کاربری مجاز کنید (برای مثال، کلیدشده با شناسه دستگاه یا نصب).
- تنظیم TTL مناسب و پاک کردن کلیدهای یتیم: حذف نصب برنامه یا پاک کردن دادههای برنامه، کلید بازیابی محلی را بدون اطلاع دادن به سرور شما حذف میکند. کلیدهای بازیابی را بهاندازه کافی نگه دارید تا در پنجرههای ارتقای دستگاه باقی بمانند (حتی اگر کاربر پساز انتقال از سیستم دستگاه قدیمی خارج شود)، و کلیدهای قدیمی را براساس TTL عدم فعالیت یا محدودیتهای کلید برای هر کاربر هرس کنید.
حذف کلید بازیابی
«مدیر اطلاعات کاربری» بدون حالت است و از فعالیت کاربر آگاه نیست، بنابراین
کلیدهای بازیابی را پساز استفاده بهطور خودکار حذف نمیکند. برای حذف کلید بازیابی، روش
clearCredentialState() را فراخوانی کنید. برای امنیت، هرگاه کاربری از سیستم خارج شد، کلید را حذف کنید تا در راهاندازی بعدی برنامه، بدون اطلاع کاربر دوباره به سیستم وارد نشود.
کلیدهای بازیابی فقط در شرایط زیر برداشته میشوند:
- کنشهای سطح سیستم: کاربران برنامه را حذف نصب میکنند یا دادههای آن را پاک میکنند.
- فراخوانیهای سطح برنامه: با فراخوانی
clearCredentialState()هنگام رسیدگی به خروج کاربر از سیستم در کد برنامهتان، کلید را بهصورت برنامهنویسیشده حذف کنید.
چون ClearCredentialStateRequest() پیشفرض فقط وضعیت جلسه استاندارد را پاک میکند و کلید بازیابی را دستنخورده باقی میگذارد، هنگام خروج از سیستم، پاکسازی دوگانه انجام دهید:
- برای پاک کردن وضعیت جلسه ارائهدهنده اطلاعات کاربری فعال،
credentialManager.clearCredentialState(ClearCredentialStateRequest())را فراخوانی کنید. - برای حذف
RestoreCredentialذخیرهشده، باcredentialManager.clearCredentialState()تماس بگیرید وClearCredentialStateRequest(ClearCredentialStateRequest.TYPE_CLEAR_RESTORE_CREDENTIAL)را ارائه دهید. - نشانگر محلی
has_synced_restore_credentialرا از فضای ذخیرهسازی محلی حذف کنید.
// Create a ClearCredentialStateRequest object
val clearRequest = ClearCredentialStateRequest(TYPE_CLEAR_RESTORE_CREDENTIAL)
// When the user logs out, delete the restore key
val response = credentialManager.clearCredentialState(clearRequest)
مراحل بعدی
- آزمایش کردن «بازگرداندن اعتبارنامهها»: آزمایش کردن جریانهای بازگرداندن ابری و دستگاهبهدستگاه (D2D) بااستفاده از Android Studio یا
adb shell bmgr. - آزمایش بازیابی و پشتیبانگیری خودکار: آزمایشهای میزبان JUnit 5 را با
BackupRestoreControllerبنویسید تا بازیابیهایCLOUD_ENCRYPTEDوDEVICE_TO_DEVICEرا در CI درستیسنجی کنید.