با پشتیبانی از گذرکلیدها، ورود به سیستم فدرال، و ارائهدهندگان اصالتسنجی طرف سوم، Credential Manager میانای برنامهسازی کاربردی توصیهشده برای اصالتسنجی در Android است که محیطی امن و راحت را فراهم میکند که به کاربران اجازه میدهد اطلاعات کاربری خود را همگامسازی و مدیریت کنند. توسعهدهندگانی که از اعتبارنامههای محلی FIDO2 استفاده میکنند باید برنامه خود را بهروز کنند تا با ادغام شدن با Credential Manager API از اصالتسنجی گذرکلید پشتیبانی کند. این سند نحوه انتقال پروژه از FIDO2 به Credential Manager را شرح میدهد.
دلایل انتقال از FIDO2 به Credential Manager
در اکثر موارد، باید ارائهدهنده اصالتسنجی برنامه Android خود را به Credential Manager منتقل کنید. دلایل انتقال به «مدیر اطلاعات کاربری» عبارتاند از:
- پشتیبانی از گذرکلید: Credential Manager از گذرکلید پشتیبانی میکند، یک سازوکار اصالتسنجی جدید و بدون گذرواژه که ایمنتر و آسانتر از گذرواژه است.
- روشهای ورود به سیستم چندگانه: Credential Manager از چندین روش ورود به سیستم، ازجمله گذرواژه، گذرکلید، و روشهای ورود به سیستم فدرالی پشتیبانی میکند. این کار اصالتسنجی کاربران در برنامه شما را، صرفنظر از روش اصالتسنجی ترجیحی آنها، آسانتر میکند.
- پشتیبانی از ارائهدهنده اطلاعات کاربری طرف سوم: در Android 14 و بالاتر، «مدیر اطلاعات کاربری» از چندین ارائهدهنده اطلاعات کاربری طرف سوم پشتیبانی میکند. این یعنی کاربران شما میتوانند از اعتبارنامههای موجود خود از ارائهدهندگان دیگر برای ورود به سیستم برنامه شما استفاده کنند.
- تجربه کاربری یکپارچه: «مدیر اطلاعات اعتباری» تجربه کاربری یکپارچهتری برای اصالتسنجی در برنامهها و سازوکارهای ورود به سیستم ارائه میدهد. این کار باعث میشود کاربران راحتتر جریان اصالتسنجی برنامهتان را درک و از آن استفاده کنند.
برای شروع انتقال از FIDO2 به Credential Manager، مراحل زیر را دنبال کنید.
بهروزرسانی وابستگیها
افزایه Kotlin را در build.gradle پروژهتان به نسخه ۱.۸.۱۰ یا بالاتر بهروز کنید.
plugins { //… id 'org.jetbrains.kotlin.android' version '1.8.10' apply false //… }در
build.gradleپروژهتان، وابستگیهایتان را بهروز کنید تا از جدیدترین نسخههای Credential Manager و کتابخانههای «اصالتسنجی خدمات Play» استفاده کنید.dependencies { // ... // Credential Manager: implementation 'androidx.credentials:credentials:<latest-version>' // Play Services Authentication: // Optional - needed for credentials support from play services, for devices running // Android 13 and lower: implementation 'androidx.credentials:credentials-play-services-auth:<latest-version>' // ... }مقداردهی اولیه FIDO را با مقداردهی اولیه «مدیر اطلاعات کاربری» جایگزین کنید. این اعلامیه را در کلاسی که برای روشهای ایجاد گذرکلید و ورود به سیستم استفاده میکنید اضافه کنید:
val credMan = CredentialManager.create(context)
ایجاد گذرکلید
باید گذرکلید جدیدی ایجاد کنید، آن را با حساب کاربر مرتبط کنید، و کلید عمومی گذرکلید را در سرورتان ذخیره کنید تا کاربر بتواند با آن به سیستم وارد شود. با بهروزرسانی فراخوانیهای تابع ثبت، برنامهتان را با این توانایی راهاندازی کنید.
برای دریافت پارامترهای لازم که درطول ایجاد گذرکلید به روش
createCredential()ارسال میشوند،name("residentKey").value("required")را (همانطور که در مشخصات WebAuthn شرح داده شده است) به فراخوانی سرورregisterRequest()خود اضافه کنید.suspend fun registerRequest() { // ... val call = client.newCall( Builder() .method( "POST", jsonRequestBody { name("attestation").value("none") name("authenticatorSelection").objectValue { name("residentKey").value("required") } } ).build() ) // ... }نوع
returnرا برایregisterRequest()و همه توابع فرزند رویJSONObjectتنظیم کنید.suspend fun registerRequest(sessionId: String): ApiResult<JSONObject> { val call = client.newCall( Builder() .url("$BASE_URL/<your api url>") .addHeader("Cookie", formatCookie(sessionId)) .method( "POST", jsonRequestBody { name("attestation").value("none") name("authenticatorSelection").objectValue { name("authenticatorAttachment").value("platform") name("userVerification").value("required") name("residentKey").value("required") } } ).build() ) val response = call.await() return response.result("Error calling the api") { parsePublicKeyCredentialCreationOptions( body ?: throw ApiException("Empty response from the api call") ) } }هر روشی را که راهانداز هدف و فراخوانیهای نتیجه فعالیت را از نمای شما مدیریت میکند بهطور ایمن بردارید.
چون
registerRequest()اکنونJSONObjectرا برمیگرداند، لازم نیستPendingIntentایجاد کنید. قصد برگرداندهشده را باJSONObjectجایگزین کنید. فراخوانیهای راهانداز قصد را بهروز کنید تاcreateCredential()را از Credential Manager API فراخوانی کند. فراخوانی روش میانای برنامهسازی کاربردیcreateCredential().suspend fun createPasskey( activity: Activity, requestResult: JSONObject ): CreatePublicKeyCredentialResponse? { val request = CreatePublicKeyCredentialRequest(requestResult.toString()) var response: CreatePublicKeyCredentialResponse? = null try { response = credMan.createCredential( request = request as CreateCredentialRequest, context = activity ) as CreatePublicKeyCredentialResponse } catch (e: CreateCredentialException) { showErrorAlert(activity, e) return null } return response }وقتی تماس موفقیتآمیز بود، پاسخ را به سرور برگردانید. درخواست و پاسخ برای این تماس مشابه پیادهسازی FIDO2 است، بنابراین هیچ تغییری لازم نیست.
اصالتسنجی با گذرکلید
پساز راهاندازی ایجاد گذرکلید، میتوانید برنامهتان را طوری راهاندازی کنید که به کاربران اجازه دهد بااستفاده از گذرکلیدهایشان به سیستم وارد شوند و اصالتسنجی کنند. برای انجام این کار، کد اصالتسنجیتان را برای مدیریت نتایج Credential Manager بهروزرسانی میکنید و تابعی را برای اصالتسنجی ازطریق گذرکلیدها پیادهسازی میکنید.
- درخواست ورود به سیستم شما با سرور تماس میگیرد تا اطلاعات لازم را برای ارسال به درخواست
getCredential()دریافت کند، که این فرایند مشابه پیادهسازی FIDO2 است. هیچ تغییری لازم نیست. مشابه فراخوانی درخواست ثبت، پاسخ برگشتی در قالب JSONObject است.
/** * @param sessionId The session ID to be used for the sign-in. * @param credentialId The credential ID of this device. * @return a JSON object. */ suspend fun signinRequest(): ApiResult<JSONObject> { val call = client.newCall( Builder().url( buildString { append("$BASE_URL/signinRequest") } ).method("POST", jsonRequestBody {}) .build() ) val response = call.await() return response.result("Error calling /signinRequest") { parsePublicKeyCredentialRequestOptions( body ?: throw ApiException("Empty response from /signinRequest") ) } } /** * @param sessionId The session ID to be used for the sign-in. * @param response The JSONObject for signInResponse. * @param credentialId id/rawId. * @return A list of all the credentials registered on the server, * including the newly-registered one. */ suspend fun signinResponse( sessionId: String, response: JSONObject, credentialId: String ): ApiResult<Unit> { val call = client.newCall( Builder().url("$BASE_URL/signinResponse") .addHeader("Cookie", formatCookie(sessionId)) .method( "POST", jsonRequestBody { name("id").value(credentialId) name("type").value(PUBLIC_KEY.toString()) name("rawId").value(credentialId) name("response").objectValue { name("clientDataJSON").value( response.getString("clientDataJSON") ) name("authenticatorData").value( response.getString("authenticatorData") ) name("signature").value( response.getString("signature") ) name("userHandle").value( response.getString("userHandle") ) } } ).build() ) val apiResponse = call.await() return apiResponse.result("Error calling /signingResponse") { } }هر روشی را که راهانداز هدف و فراخوانیهای نتیجه فعالیت را مدیریت میکند بهطور ایمن از نمای خود بردارید.
ازآنجاییکه
signInRequest()اکنونJSONObjectرا برمیگرداند، نیازی به ایجادPendingIntentندارید. قصد برگرداندهشده را باJSONObjectجایگزین کنید وgetCredential()را از روشهای میانای برنامهسازی کاربردیتان فراخوانی کنید.suspend fun getPasskey( activity: Activity, creationResult: JSONObject ): GetCredentialResponse? { Toast.makeText( activity, "Fetching previously stored credentials", Toast.LENGTH_SHORT ) .show() var result: GetCredentialResponse? = null try { val request = GetCredentialRequest( listOf( GetPublicKeyCredentialOption( creationResult.toString(), null ), GetPasswordOption() ) ) result = credMan.getCredential(activity, request) if (result.credential is PublicKeyCredential) { val publicKeycredential = result.credential as PublicKeyCredential Log.i("TAG", "Passkey ${publicKeycredential.authenticationResponseJson}") return result } } catch (e: Exception) { showErrorAlert(activity, e) } return result }وقتی تماس موفقیتآمیز بود، پاسخ را به سرور برگردانید تا کاربر را اعتبارسنجی و اصالتسنجی کند. پارامترهای درخواست و پاسخ برای این فراخوانی میانای برنامهسازی کاربردی مشابه پیادهسازی FIDO2 است، بنابراین هیچ تغییری لازم نیست.
منابع بیشتر
- مرجع نمونه Credential Manager
- Credential Manager Codelab
- ارائه اصالتسنجی یکپارچه به برنامههایتان با گذرکلید بااستفاده از Credential Manager API
- آزمایشگاه کد FIDO2