Credential Manager به مجموعهای از APIهای معرفیشده در Android 14 اشاره دارد که از روشهای ورود به سیستم متعدد مانند نام کاربری-گذرواژه، کلیدهای عبور و راهحلهای ورود به سیستم فدرال (مانند ورود با Google) پشتیبانی میکنند. هنگامی که Credential Manager API فراخوانی می شود، سیستم Android اعتبارنامه ها را از همه ارائه دهندگان اعتبار نصب شده در دستگاه جمع می کند. این سند مجموعه ای از API ها را توصیف می کند که نقاط پایانی یکپارچه سازی را برای این ارائه دهندگان اعتبار ارائه می کنند.
راه اندازی
قبل از اجرای عملکرد در ارائه دهنده اعتبار خود، مراحل راه اندازی نشان داده شده در بخش های زیر را کامل کنید.
وابستگی ها را اعلام کنید
در فایل build.gradle
ماژول خود، یک وابستگی را با استفاده از آخرین نسخه کتابخانه Credential Manager اعلام کنید:
implementation "androidx.credentials:credentials:1.2.0-{latest}"
عنصر سرویس را در فایل مانیفست اعلام کنید
در فایل مانیفست برنامه خود AndroidManifest.xml
، یک اعلان <service>
برای یک کلاس سرویس اضافه کنید که کلاس CredentialProviderService
را از کتابخانه androidx.credentials گسترش می دهد، همانطور که در مثال زیر نشان داده شده است.
<service android:name=".MyCredentialProviderService"
android:enabled="true"
android:exported="true"
android:label="My Credential Provider"
android:icon="<any drawable icon>"
android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE">
<intent-filter>
<action android:name="android.service.credentials.CredentialProviderService"/>
</intent-filter>
<meta-data
android:name="android.credentials.provider"
android:resource="@xml/provider"/>
</service>
مجوز و فیلتر قصد نشان داده شده در بالا برای جریان مدیر اعتبارنامه برای کار همانطور که انتظار می رود ضروری است. مجوز لازم است تا فقط سیستم اندروید بتواند به این سرویس متصل شود. فیلتر قصد برای شناسایی این سرویس به عنوان یک ارائه دهنده اعتبار برای استفاده توسط Credential Manager استفاده می شود.
انواع اعتبارنامه های پشتیبانی شده را اعلام کنید
در پوشه res/xml
خود، یک فایل جدید به نام provider.xml
ایجاد کنید. در این فایل، انواع اعتبارنامه هایی را که سرویس شما پشتیبانی می کند، از طریق ثابت هایی که برای هر نوع اعتبار در کتابخانه تعریف شده است، اعلام کنید. در مثال زیر، این سرویس از رمزهای عبور سنتی و همچنین کلیدهای عبور پشتیبانی میکند، ثابتهایی که برای آنها به صورت TYPE_PASSWORD_CREDENTIAL
و TYPE_PUBLIC_KEY_CREDENTIAL
تعریف شدهاند:
<?xml version="1.0" encoding="utf-8"?>
<credential-provider xmlns:android="http://schemas.android.com/apk/res/android">
<capabilities>
<capability name="android.credentials.TYPE_PASSWORD_CREDENTIAL" />
<capability name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
</capabilities>
</credential-provider>
در سطوح قبلی API، ارائهدهندگان اعتبار با APIهایی مانند تکمیل خودکار برای گذرواژهها و سایر دادهها ادغام میشوند. این ارائه دهندگان می توانند از همان زیرساخت داخلی برای ذخیره انواع اعتبارنامه های موجود استفاده کنند، در حالی که آن را برای پشتیبانی از سایرین، از جمله کلیدهای عبور، گسترش دهند.
رویکرد دو مرحله ای برای تعامل با ارائه دهنده
مدیر اعتبار با ارائه دهندگان اعتبار در دو مرحله تعامل دارد:
- مرحله اول مرحله شروع/پرس و جو است که در آن سیستم به خدمات ارائه دهنده اعتبار متصل می شود و متدهای
onBeginGetCredentialRequest()
,onBeginCreateCredentialRequest()
یاonClearCredentialStateRequest()
را با درخواست هایBegin…
فراخوانی می کند. ارائهدهندگان باید این درخواستها را پردازش کنند و با پاسخهایBegin…
پاسخ دهند و آنها را با ورودیهایی پر کنند که نشاندهنده گزینههای بصری برای نمایش در انتخابگر حساب هستند. هر ورودی باید یک مجموعهPendingIntent
داشته باشد. - هنگامی که کاربر یک ورودی را انتخاب کرد، مرحله انتخاب شروع می شود و
PendingIntent
مرتبط با ورودی فعال می شود و فعالیت ارائه دهنده مربوطه را نشان می دهد. هنگامی که کاربر تعامل با این فعالیت را تمام کرد، ارائه دهنده اعتبار باید قبل از پایان فعالیت، پاسخ را به نتیجه فعالیت تنظیم کند. سپس این پاسخ به برنامه مشتری که Credential Manager را فراخوانی کرده است ارسال می شود.
ایجاد کلید عبور را مدیریت کنید
رسیدگی به پرس و جوها برای ایجاد رمز عبور
هنگامی که یک برنامه مشتری می خواهد یک رمز عبور ایجاد کند و آن را با یک ارائه دهنده اعتبار ذخیره کند، createCredential
API را صدا می کند. برای رسیدگی به این درخواست در سرویس ارائه دهنده اعتبار خود به گونه ای که رمز عبور واقعاً در فضای ذخیره سازی شما ذخیره می شود، مراحل نشان داده شده در بخش های زیر را کامل کنید.
- روش
onBeginCreateCredentialRequest()
در سرویس خود که ازCredentialProviderService
توسعه داده شده است، لغو کنید. - با ساختن یک
BeginCreateCredentialResponse
مربوطه و ارسال آن از طریق callback،BeginCreateCredentialRequest
را مدیریت کنید. - هنگام ساخت
BeginCreateCredentialResponse
،CreateEntries
مورد نیاز را اضافه کنید. هرCreateEntry
باید با حسابی مطابقت داشته باشد که در آن اعتبارنامه میتواند ذخیره شود، و باید یک مجموعهPendingIntent
به همراه سایر متادیتاهای مورد نیاز داشته باشد.
مثال زیر نحوه اجرای این مراحل را نشان می دهد.
override fun onBeginCreateCredentialRequest(
request: BeginCreateCredentialRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,
) {
val response: BeginCreateCredentialResponse? = processCreateCredentialRequest(request)
if (response != null) {
callback.onResult(response)
} else {
callback.onError(CreateCredentialUnknownException())
}
}
fun processCreateCredentialRequest(request: BeginCreateCredentialRequest): BeginCreateCredentialResponse? {
when (request) {
is BeginCreatePublicKeyCredentialRequest -> {
// Request is passkey type
return handleCreatePasskeyQuery(request)
}
}
// Request not supported
return null
}
private fun handleCreatePasskeyQuery(
request: BeginCreatePublicKeyCredentialRequest
): BeginCreateCredentialResponse {
// Adding two create entries - one for storing credentials to the 'Personal'
// account, and one for storing them to the 'Family' account. These
// accounts are local to this sample app only.
val createEntries: MutableList<CreateEntry> = mutableListOf()
createEntries.add( CreateEntry(
PERSONAL_ACCOUNT_ID,
createNewPendingIntent(PERSONAL_ACCOUNT_ID, CREATE_PASSKEY_INTENT)
))
createEntries.add( CreateEntry(
FAMILY_ACCOUNT_ID,
createNewPendingIntent(FAMILY_ACCOUNT_ID, CREATE_PASSKEY_INTENT)
))
return BeginCreateCredentialResponse(createEntries)
}
private fun createNewPendingIntent(accountId: String, action: String): PendingIntent {
val intent = Intent(action).setPackage(PACKAGE_NAME)
// Add your local account ID as an extra to the intent, so that when
// user selects this entry, the credential can be saved to this
// account
intent.putExtra(EXTRA_KEY_ACCOUNT_ID, accountId)
return PendingIntent.getActivity(
applicationContext, UNIQUE_REQ_CODE,
intent, (
PendingIntent.FLAG_MUTABLE
or PendingIntent.FLAG_UPDATE_CURRENT
)
)
}
ساخت و ساز PendingIntent
شما باید از موارد زیر پیروی کند:
- فعالیت مربوطه باید تنظیم شود تا هرگونه درخواست بیومتریک، تأیید یا انتخاب مورد نیاز را نشان دهد.
- هر گونه داده مورد نیازی که ارائهدهنده هنگام فراخوانی فعالیت مربوطه به آن نیاز دارد، باید بهعنوان یک هدف اضافی در مورد هدفی که برای ایجاد
PendingIntent
شما استفاده میشود، تنظیم شود، مانندaccountId
در جریان ایجاد. -
PendingIntent
شما باید با پرچمPendingIntent.FLAG_MUTABLE
ساخته شود تا سیستم بتواند درخواست نهایی را به intent اضافه کند. -
PendingIntent
شما نباید با پرچمPendingIntent.FLAG_ONE_SHOT
ساخته شود.FLAG_ONE_SHOT زیرا کاربر ممکن است یک ورودی را انتخاب کند، به عقب برگردد و آن را دوباره انتخاب کند که باعث می شودPendingIntent
دو بار فعال شود. -
PendingIntent
شما باید با یک کد درخواست منحصر به فرد ساخته شود تا هر ورودی بتواندPendingIntent
مربوط به خود را داشته باشد.
انتخاب ورودی برای درخواستهای ایجاد رمز عبور را مدیریت کنید
- هنگامی که کاربر یک
CreateEntry
را انتخاب می کند که قبلاً پر شده است،PendingIntent
مربوطه فراخوانی می شود وActivity
ارائه دهنده مرتبط ایجاد می شود. - پس از فراخوانی متد
onCreate
از Activity شما، به intent مرتبط دسترسی پیدا کنید و آن را به کلاسPendingIntentHander
ارسال کنید تاProviderCreateCredentialRequest
را دریافت کنید. -
requestJson
،callingAppInfo
وclientDataHash
از درخواست استخراج کنید. -
accountId
محلی را از intent extra استخراج کنید. این یک نمونه اجرای خاص برنامه است و مورد نیاز نیست. این شناسه حساب می تواند برای ذخیره این اعتبار در برابر این شناسه حساب خاص استفاده شود. -
requestJson
را اعتبارسنجی کنید. مثال زیر از کلاسهای داده محلی مانندPublicKeyCredentialCreationOptions
برای تبدیل JSON ورودی به یک کلاس ساختاریافته طبق مشخصات WebAuthn استفاده میکند. به عنوان یک ارائه دهنده اعتبار، می توانید آن را با تجزیه کننده خود جایگزین کنید. - اگر تماس از یک برنامه Android بومی است ، پیوند دارایی را برای برنامه تماس بررسی کنید.
- یک درخواست احراز هویت را در سطح قرار دهید. مثال زیر از Android Biometric API استفاده می کند.
- وقتی احراز هویت با موفقیت انجام شد، یک
credentialId
و یک جفت کلید ایجاد کنید. - کلید خصوصی را در پایگاه داده محلی خود در برابر
callingAppInfo.packageName
ذخیره کنید. - یک پاسخ JSON API Authentication Web بسازید که از کلید عمومی و
credentialId
تشکیل شده است. مثال زیر از کلاس های ابزار محلی مانندAuthenticatorAttestationResponse
وFidoPublicKeyCredential
استفاده می کند که به ساخت JSON بر اساس مشخصات ذکر شده قبلی کمک می کند. به عنوان یک ارائه دهنده اعتبار، می توانید این کلاس ها را با سازنده های خود جایگزین کنید. - با JSON تولید شده در بالا یک
CreatePublicKeyCredentialResponse
بسازید. - از طریق
PendingIntentHander.setCreateCredentialResponse()
CreatePublicKeyCredentialResponse
به عنوان یکIntent
اضافه کنید و آن را روی نتیجه Activity تنظیم کنید. - فعالیت را تمام کنید.
مثال کد زیر این مراحل را نشان می دهد. این کد پس از فراخوانی onCreate()
باید در کلاس Activity شما مدیریت شود.
val request =
PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
val accountId = intent.getStringExtra(CredentialsRepo.EXTRA_KEY_ACCOUNT_ID)
if (request != null && request.callingRequest is CreatePublicKeyCredentialRequest) {
val publicKeyRequest: CreatePublicKeyCredentialRequest =
request.callingRequest as CreatePublicKeyCredentialRequest
createPasskey(
publicKeyRequest.requestJson,
request.callingAppInfo,
publicKeyRequest.clientDataHash,
accountId
)
}
fun createPasskey(
requestJson: String,
callingAppInfo: CallingAppInfo?,
clientDataHash: ByteArray?,
accountId: String?
) {
val request = PublicKeyCredentialCreationOptions(requestJson)
val biometricPrompt = BiometricPrompt(
this,
<executor>,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(
errorCode: Int, errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
finish()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
finish()
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
// Generate a credentialId
val credentialId = ByteArray(32)
SecureRandom().nextBytes(credentialId)
// Generate a credential key pair
val spec = ECGenParameterSpec("secp256r1")
val keyPairGen = KeyPairGenerator.getInstance("EC");
keyPairGen.initialize(spec)
val keyPair = keyPairGen.genKeyPair()
// Save passkey in your database as per your own implementation
// Create AuthenticatorAttestationResponse object to pass to
// FidoPublicKeyCredential
val response = AuthenticatorAttestationResponse(
requestOptions = request,
credentialId = credentialId,
credentialPublicKey = getPublicKeyFromKeyPair(keyPair),
origin = appInfoToOrigin(callingAppInfo),
up = true,
uv = true,
be = true,
bs = true,
packageName = callingAppInfo.packageName
)
val credential = FidoPublicKeyCredential(
rawId = credentialId, response = response
)
val result = Intent()
val createPublicKeyCredResponse =
CreatePublicKeyCredentialResponse(credential.json())
// Set the CreateCredentialResponse as the result of the Activity
PendingIntentHandler.setCreateCredentialResponse(
result, createPublicKeyCredResponse
)
setResult(Activity.RESULT_OK, result)
finish()
}
}
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Use your screen lock")
.setSubtitle("Create passkey for ${request.rp.name}")
.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG
/* or BiometricManager.Authenticators.DEVICE_CREDENTIAL */
)
.build()
biometricPrompt.authenticate(promptInfo)
}
fun appInfoToOrigin(info: CallingAppInfo): String {
val cert = info.signingInfo.apkContentsSigners[0].toByteArray()
val md = MessageDigest.getInstance("SHA-256");
val certHash = md.digest(cert)
// This is the format for origin
return "android:apk-key-hash:${b64Encode(certHash)}"
}
رسیدگی به درخواستهای ایجاد رمز عبور
برای رسیدگی به درخواستهای ایجاد رمز عبور، موارد زیر را انجام دهید:
- در روش
processCreateCredentialRequest()
که در بخش قبل ذکر شد، یک مورد دیگر در بلوک سوئیچ برای رسیدگی به درخواستهای رمز عبور اضافه کنید. - هنگام ساخت
BeginCreateCredentialResponse
،CreateEntries
مورد نیاز را اضافه کنید. - هر
CreateEntry
باید با حسابی مطابقت داشته باشد که اعتبارنامه را می توان در آن ذخیره کرد و باید یک مجموعهPendingIntent
به همراه سایر ابرداده ها روی آن تنظیم شود.
مثال زیر نحوه اجرای این مراحل را نشان می دهد:
fun processCreateCredentialRequest(
request: BeginCreateCredentialRequest
): BeginCreateCredentialResponse? {
when (request) {
is BeginCreatePublicKeyCredentialRequest -> {
// Request is passkey type
return handleCreatePasskeyQuery(request)
}
is BeginCreatePasswordCredentialRequest -> {
// Request is password type
return handleCreatePasswordQuery(request)
}
}
return null
}
private fun handleCreatePasswordQuery(
request: BeginCreatePasswordCredentialRequest
): BeginCreateCredentialResponse {
val createEntries: MutableList<CreateEntry> = mutableListOf()
// Adding two create entries - one for storing credentials to the 'Personal'
// account, and one for storing them to the 'Family' account. These
// accounts are local to this sample app only.
createEntries.add(
CreateEntry(
PERSONAL_ACCOUNT_ID,
createNewPendingIntent(PERSONAL_ACCOUNT_ID, CREATE_PASSWORD_INTENT)
)
)
createEntries.add(
CreateEntry(
FAMILY_ACCOUNT_ID,
createNewPendingIntent(FAMILY_ACCOUNT_ID, CREATE_PASSWORD_INTENT)
)
)
return BeginCreateCredentialResponse(createEntries)
}
انتخاب ورودی برای درخواست های ایجاد رمز عبور را مدیریت کنید
هنگامی که کاربر یک CreateEntry
پر شده را انتخاب می کند، PendingIntent
مربوطه، Activity مرتبط را اجرا کرده و نمایش می دهد. به intent مرتبط ارسال شده در onCreate
دسترسی پیدا کنید و آن را به کلاس PendingIntentHander
ارسال کنید تا متد ProviderCreateCredentialRequest
را دریافت کنید.
مثال زیر نحوه اجرای این فرآیند را نشان می دهد. این کد باید در متد onCreate()
Activity شما مدیریت شود.
val createRequest = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
val accountId = intent.getStringExtra(CredentialsRepo.EXTRA_KEY_ACCOUNT_ID)
val request: CreatePasswordRequest = createRequest.callingRequest as CreatePasswordRequest
// Fetch the ID and password from the request and save it in your database
<your_database>.addNewPassword(
PasswordInfo(
request.id,
request.password,
createRequest.callingAppInfo.packageName
)
)
//Set the final response back
val result = Intent()
val response = CreatePasswordResponse()
PendingIntentHandler.setCreateCredentialResponse(result, response)
setResult(Activity.RESULT_OK, result)
this@<activity>.finish()
ورود کاربر را کنترل کنید
ورود کاربر با مراحل زیر انجام می شود:
- هنگامی که یک برنامه مشتری سعی می کند یک کاربر را وارد کند ، یک نمونه
GetCredentialRequest
آماده می کند. - چارچوب Android این درخواست را با اتصال به این خدمات به همه ارائه دهندگان اعتبار قابل اجرا منتشر می کند.
- سپس سرویس ارائه دهنده یک
BeginGetCredentialRequest
دریافت می کند که حاوی لیستی ازBeginGetCredentialOption
است، که هر کدام شامل پارامترهایی است که می تواند برای بازیابی اعتبارنامه های منطبق استفاده شود.
برای رسیدگی به این درخواست در سرویس ارائه دهنده اعتبار خود، مراحل زیر را انجام دهید:
برای رسیدگی به درخواست، متد
onBeginGetCredentialRequest()
را لغو کنید. توجه داشته باشید که اگر اطلاعات کاربری شما قفل شده است، میتوانید فوراً یکAuthenticationAction
را روی پاسخ تنظیم کنید و پاسخ تماس را فراخوانی کنید.private val unlockEntryTitle = "Authenticate to continue" override fun onBeginGetCredentialRequest( request: BeginGetCredentialRequest, cancellationSignal: CancellationSignal, callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>, ) { if (isAppLocked()) { callback.onResult(BeginGetCredentialResponse( authenticationActions = mutableListOf(AuthenticationAction( unlockEntryTitle, createUnlockPendingIntent()) ) ) ) return } try { response = processGetCredentialRequest(request) callback.onResult(response) } catch (e: GetCredentialException) { callback.onError(GetCredentialUnknownException()) } }
ارائهدهندگانی که قبل از بازگرداندن هر
credentialEntries
اعتبارنامه نیاز به باز کردن قفل آن دارند، باید یک هدف معلق تنظیم کنند که کاربر را به جریان باز کردن قفل برنامه هدایت کند:private fun createUnlockPendingIntent(): PendingIntent { val intent = Intent(UNLOCK_INTENT).setPackage(PACKAGE_NAME) return PendingIntent.getActivity( applicationContext, UNIQUE_REQUEST_CODE, intent, ( PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) ) }
اعتبارنامه ها را از پایگاه داده محلی خود بازیابی کنید و آنها را با استفاده از
CredentialEntries
تنظیم کنید تا در انتخابگر نشان داده شوند. برای کلیدهای عبور، میتوانیدcredentialId
بهعنوان یک مورد اضافی در intent تنظیم کنید تا بدانید وقتی کاربر این ورودی را انتخاب میکند به کدام اعتبار نگاشت میشود.companion object { // These intent actions are specified for corresponding activities // that are to be invoked through the PendingIntent(s) private const val GET_PASSKEY_INTENT_ACTION = "PACKAGE_NAME.GET_PASSKEY" private const val GET_PASSWORD_INTENT_ACTION = "PACKAGE_NAME.GET_PASSWORD" } fun processGetCredentialsRequest( request: BeginGetCredentialRequest ): BeginGetCredentialResponse { val callingPackage = request.callingAppInfo?.packageName val credentialEntries: MutableList<CredentialEntry> = mutableListOf() for (option in request.beginGetCredentialOptions) { when (option) { is BeginGetPasswordOption -> { credentialEntries.addAll( populatePasswordData( callingPackage, option ) ) } is BeginGetPublicKeyCredentialOption -> { credentialEntries.addAll( populatePasskeyData( callingPackage, option ) ) ) } else -> { Log.i(TAG, "Request not supported") } } } return BeginGetCredentialResponse(credentialEntries) }
اعتبارنامه ها را از پایگاه داده خود جستجو کنید، ورودی های رمز عبور و رمز عبور را برای پر کردن ایجاد کنید.
private fun populatePasskeyData( callingAppInfo: CallingAppInfo, option: BeginGetPublicKeyCredentialOption ): List<CredentialEntry> { val passkeyEntries: MutableList<CredentialEntry> = mutableListOf() val request = PublicKeyCredentialRequestOptions(option.requestJson) // Get your credentials from database where you saved during creation flow val creds = <getCredentialsFromInternalDb(request.rpId)> val passkeys = creds.passkeys for (passkey in passkeys) { val data = Bundle() data.putString("credId", passkey.credId) passkeyEntries.add( PublicKeyCredentialEntry( context = applicationContext, username = passkey.username, pendingIntent = createNewPendingIntent( GET_PASSKEY_INTENT_ACTION, data ), beginPublicKeyCredentialOption = option, displayName = passkey.displayName, icon = passkey.icon ) ) } return passkeyEntries } // Fetch password credentials and create password entries to populate to // the user private fun populatePasswordData( callingPackage: String, option: BeginGetPasswordOption ): List<CredentialEntry> { val passwordEntries: MutableList<CredentialEntry> = mutableListOf() // Get your password credentials from database where you saved during // creation flow val creds = <getCredentialsFromInternalDb(callingPackage)> val passwords = creds.passwords for (password in passwords) { passwordEntries.add( PasswordCredentialEntry( context = applicationContext, username = password.username, pendingIntent = createNewPendingIntent( GET_PASSWORD_INTENT ), beginGetPasswordOption = option displayName = password.username, icon = password.icon ) ) } return passwordEntries } private fun createNewPendingIntent( action: String, extra: Bundle? = null ): PendingIntent { val intent = Intent(action).setPackage(PACKAGE_NAME) if (extra != null) { intent.putExtra("CREDENTIAL_DATA", extra) } return PendingIntent.getActivity( applicationContext, UNIQUE_REQUEST_CODE, intent, (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) ) }
پس از پرس و جو و پر کردن اطلاعات کاربری، اکنون باید مرحله انتخاب اعتبارنامه هایی که توسط کاربر انتخاب می شوند انجام دهید، خواه رمز عبور یا رمز عبور باشد.
مدیریت انتخاب کاربر برای کلیدهای عبور
- در متد
onCreate
از Activity مربوطه، intent مرتبط را بازیابی کنید و بهPendingIntentHandler.retrieveProviderGetCredentialRequest()
منتقل کنید. -
GetPublicKeyCredentialOption
را از درخواست بازیابی شده در بالا استخراج کنید. سپسrequestJson
وclientDataHash
را از این گزینه استخراج کنید. -
credentialId
از intent extra، که توسط ارائهدهنده اعتبار زمانی کهPendingIntent
مربوطه راهاندازی شد، پر شده است، استخراج کنید. - با استفاده از پارامترهای درخواستی که در بالا به آن دسترسی دارید، کلید عبور را از پایگاه داده محلی خود استخراج کنید.
تأیید کنید که کلید عبور با ابرداده استخراج شده و تأیید کاربر معتبر است.
val getRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent) val publicKeyRequest = getRequest.credentialOption as GetPublicKeyCredentialOption val requestInfo = intent.getBundleExtra("CREDENTIAL_DATA") val credIdEnc = requestInfo.getString("credId") // Get the saved passkey from your database based on the credential ID // from the publickeyRequest val passkey = <your database>.getPasskey(credIdEnc) // Decode the credential ID, private key and user ID val credId = b64Decode(credIdEnc) val privateKey = b64Decode(passkey.credPrivateKey) val uid = b64Decode(passkey.uid) val origin = appInfoToOrigin(getRequest.callingAppInfo) val packageName = getRequest.callingAppInfo.packageName validatePasskey( publicKeyRequest.requestJson, origin, packageName, uid, passkey.username, credId, privateKey )
برای تأیید اعتبار کاربر، یک اعلان بیومتریک (یا روش ادعایی دیگر) را نشان دهید. قطعه کد زیر از Android Biometric API استفاده می کند.
هنگامی که احراز هویت با موفقیت انجام شد، یک پاسخ JSON بر اساس مشخصات W3 Web Authentication Assertion بسازید. در قطعه کد زیر، کلاسهای داده کمکی مانند
AuthenticatorAssertionResponse
برای دریافت پارامترهای ساختاریافته و تبدیل آنها به فرمت JSON مورد نیاز استفاده میشوند. پاسخ حاوی یک امضای دیجیتال از کلید خصوصی یک اعتبار WebAuthn است. سرور طرف متکی میتواند این امضا را برای احراز هویت یک کاربر قبل از ورود به سیستم تأیید کند.یک
PublicKeyCredential
با استفاده از JSON تولید شده در بالا بسازید و آن را رویGetCredentialResponse
نهایی تنظیم کنید. این پاسخ نهایی را روی نتیجه این فعالیت تنظیم کنید.
مثال زیر نحوه اجرای این مراحل را نشان می دهد:
val request = PublicKeyCredentialRequestOptions(requestJson)
val privateKey: ECPrivateKey = convertPrivateKey(privateKeyBytes)
val biometricPrompt = BiometricPrompt(
this,
<executor>,
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(
errorCode: Int, errString: CharSequence
) {
super.onAuthenticationError(errorCode, errString)
finish()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
finish()
}
override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult
) {
super.onAuthenticationSucceeded(result)
val response = AuthenticatorAssertionResponse(
requestOptions = request,
credentialId = credId,
origin = origin,
up = true,
uv = true,
be = true,
bs = true,
userHandle = uid,
packageName = packageName
)
val sig = Signature.getInstance("SHA256withECDSA");
sig.initSign(privateKey)
sig.update(response.dataToSign())
response.signature = sig.sign()
val credential = FidoPublicKeyCredential(
rawId = credId, response = response
)
val result = Intent()
val passkeyCredential = PublicKeyCredential(credential.json)
PendingIntentHandler.setGetCredentialResponse(
result, GetCredentialResponse(passkeyCredential)
)
setResult(RESULT_OK, result)
finish()
}
}
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Use your screen lock")
.setSubtitle("Use passkey for ${request.rpId}")
.setAllowedAuthenticators(
BiometricManager.Authenticators.BIOMETRIC_STRONG
/* or BiometricManager.Authenticators.DEVICE_CREDENTIAL */
)
.build()
biometricPrompt.authenticate(promptInfo)
مدیریت انتخاب کاربر برای احراز هویت رمز عبور
- در فعالیت مربوطه خود، به intent ارسال شده برای
onCreate
دسترسی پیدا کنید وProviderGetCredentialRequest
را با استفاده ازPendingIntentHandler
استخراج کنید. از
GetPasswordOption
در درخواست برای بازیابی اعتبار رمز عبور برای نام بسته ورودی استفاده کنید.val getRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent) val passwordOption = getRequest.credentialOption as GetPasswordCredentialOption val username = passwordOption.username // Fetch the credentials for the calling app package name val creds = <your_database>.getCredentials(callingAppInfo.packageName) val passwords = creds.passwords val it = passwords.iterator() var password = "" while (it.hasNext() == true) { val passwordItemCurrent = it.next() if (passwordItemCurrent.username == username) { password = passwordItemCurrent.password break } }
پس از بازیابی، پاسخ را برای اعتبار رمز عبور انتخاب شده تنظیم کنید.
// Set the response back val result = Intent() val passwordCredential = PasswordCredential(username, password) PendingIntentHandler.setGetCredentialResponse( result, GetCredentialResponse(passwordCredential) ) setResult(Activity.RESULT_OK, result) finish()
انتخاب یک ورودی اقدام احراز هویت را مدیریت کنید
همانطور که قبلاً ذکر شد ، در صورتی که اعتبارنامه ها قفل شده باشند، یک ارائه دهنده اعتبار می تواند یک AuthenticationAction
تنظیم کند. اگر کاربر این ورودی را انتخاب کند، Activity مربوط به مجموعه اقدام قصد در PendingIntent
فراخوانی می شود. سپس ارائه دهندگان اعتبارنامه می توانند یک جریان احراز هویت بیومتریک یا مکانیسم مشابهی را برای باز کردن قفل اعتبارنامه ها ارائه کنند. در صورت موفقیت، ارائهدهنده اعتبار باید یک BeginGetCredentialResponse
بسازد، مشابه نحوه مدیریت ورود به سیستم کاربر در بالا ، زیرا اعتبارنامهها اکنون باز شدهاند. سپس این پاسخ باید از طریق متد PendingIntentHandler.setBeginGetCredentialResponse()
تنظیم شود قبل از اینکه هدف آماده شده به عنوان نتیجه تنظیم شود و فعالیت تمام شود.
درخواست های اعتبارنامه را پاک کنید
یک برنامه مشتری ممکن است درخواست کند که هر حالتی که برای انتخاب اعتبار حفظ می شود باید پاک شود، مانند ارائه دهنده اعتبارنامه ممکن است اعتبار انتخاب شده قبلی را به خاطر بسپارد و فقط دفعه بعد آن را برگرداند. یک برنامه مشتری این API را فراخوانی می کند و انتظار دارد که انتخاب چسبنده پاک شود. سرویس ارائهدهنده اعتبار شما میتواند این درخواست را با نادیده گرفتن روش onClearCredentialStateRequest()
رسیدگی کند:
override fun onClearCredentialStateRequest(
request: android.service.credentials.ClearCredentialStateRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<Void?, ClearCredentialException>,
) {
// Delete any maintained state as appropriate.
}
قابلیت پیوند دادن به صفحه تنظیمات ارائه دهنده خود را اضافه کنید
برای اینکه به کاربرانتان اجازه دهید تنظیمات ارائهدهنده شما را از صفحه گذرواژهها، کلیدهای عبور و تکمیل خودکار باز کنند، برنامههای ارائهدهنده اعتبار باید ویژگی settingsActivity
credential-provider
در res/xml/provider.xml
پیادهسازی کنند. اگر کاربر روی نام ارائهدهنده در لیست گذرواژهها، کلیدهای عبور و تکمیل خودکار سرویسها کلیک کند، این ویژگی به شما امکان میدهد از یک قصد برای باز کردن صفحه تنظیمات برنامه خود استفاده کنید. مقدار این ویژگی را به نام فعالیتی که از صفحه تنظیمات راه اندازی می شود، تنظیم کنید.
<credential-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:settingsSubtitle="Example settings provider name"
android:settingsActivity="com.example.SettingsActivity">
<capabilities>
<capability name="android.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
</capabilities>
</credential-provider>
اهداف تنظیمات
تنظیمات باز : هدف android.settings.CREDENTIAL_PROVIDER
صفحه تنظیماتی را نشان می دهد که در آن کاربر می تواند ارائه دهندگان اعتبار مورد نظر و اضافی خود را انتخاب کند.
سرویس اعتبار ترجیحی : هدف ACTION_REQUEST_SET_AUTOFILL_SERVICE
کاربر شما را به صفحه انتخاب ارائه دهنده ترجیحی هدایت می کند. ارائهدهنده انتخابشده در این صفحه به ارائهدهنده اعتبار و تکمیل خودکار ترجیحی تبدیل میشود.
یک لیست مجاز از برنامه های دارای امتیاز دریافت کنید
برنامههای دارای امتیاز مانند مرورگرهای وب با تنظیم پارامتر origin
در متدهای Credential Manager GetCredentialRequest()
و CreatePublicKeyCredentialRequest()
تماسهای Credential Manager را از طرف دیگر طرفهای متکی انجام میدهند. برای پردازش این درخواستها، ارائهدهنده اعتبار origin
را با استفاده از getOrigin()
API بازیابی میکند.
برای بازیابی origin
، برنامه ارائهدهنده اعتبار باید فهرستی از تماسگیرندگان ممتاز و قابل اعتماد را به API androidx.credentials.provider.CallingAppInfo's getOrigin()
ارسال کند. این لیست مجاز باید یک شی JSON معتبر باشد. در صورتی که اثر انگشت packageName
و گواهینامه به دست آمده از signingInfo
با برنامه موجود در privilegedAllowlist
ارسال شده به API getOrigin()
مطابقت داشته باشد، origin
برگردانده می شود. پس از به دست آمدن مقدار origin
، برنامه ارائه دهنده باید این را یک تماس ممتاز در نظر بگیرد و به جای محاسبه origin
با استفاده از امضای برنامه تماس، این origin
روی داده های سرویس گیرنده در AuthenticatorResponse
تنظیم کند.
اگر origin
بازیابی میکنید، از clientDataHash
که مستقیماً در CreatePublicKeyCredentialRequest()
یا GetPublicKeyCredentialOption()
ارائه شده است، به جای مونتاژ و هش کردن clientDataJSON
در طول درخواست امضا استفاده کنید. برای جلوگیری از مشکلات تجزیه JSON، یک مقدار جای نگهدار برای clientDataJSON
در پاسخ گواه و ادعا تنظیم کنید. Google Password Manager از یک لیست مجاز در دسترس برای تماس با getOrigin()
استفاده می کند. بهعنوان یک ارائهدهنده اعتبار، میتوانید از این فهرست استفاده کنید یا فهرست خود را در قالب JSON که توسط API توصیف شده است ارائه دهید. انتخاب لیست مورد استفاده به عهده ارائه دهنده است. برای دسترسی ممتاز به ارائه دهندگان اعتبار شخص ثالث، به اسناد ارائه شده توسط شخص ثالث مراجعه کنید.
ارائه دهندگان را در یک دستگاه فعال کنید
کاربران باید از طریق تنظیمات دستگاه > گذرواژهها و حسابها > ارائهدهنده شما > فعال یا غیرفعال کردن ارائهدهنده را فعال کنند.
fun createSettingsPendingIntent(): PendingIntent