کاربر خود را با Credential Manager وارد کنید

Credential Manager یک API Jetpack است که از چندین روش ورود به سیستم مانند نام کاربری و رمز عبور، کلیدهای عبور و راه حل های ورود به سیستم فدرال (مانند ورود با Google) در یک API واحد پشتیبانی می کند، بنابراین یکپارچگی را برای توسعه دهندگان ساده می کند.

علاوه بر این، برای کاربران، Credential Manager رابط ورود به سیستم را در روش‌های احراز هویت یکپارچه می‌کند و ورود کاربران به برنامه‌ها را بدون توجه به روشی که انتخاب می‌کنند، واضح‌تر و آسان‌تر می‌کند.

این صفحه مفهوم کلیدهای عبور و مراحل اجرای پشتیبانی سمت سرویس گیرنده برای راه حل های احراز هویت، از جمله کلیدهای عبور را با استفاده از Credential Manager توضیح می دهد. همچنین یک صفحه سؤالات متداول جداگانه وجود دارد که به سؤالات دقیق تر و خاص پاسخ می دهد.

بازخورد شما بخش مهمی در بهبود Credential Manager API است. با استفاده از پیوند زیر هر مشکلی را که پیدا کردید یا ایده ای برای بهبود API پیدا کردید به اشتراک بگذارید:

بازخورد بدهید

درباره کلیدهای عبور

کلیدهای عبور جایگزینی امن تر و آسان تر برای رمزهای عبور هستند. با کلیدهای عبور، کاربران می توانند با استفاده از حسگر بیومتریک (مانند اثر انگشت یا تشخیص چهره)، پین یا الگوی وارد برنامه ها و وب سایت ها شوند. این یک تجربه ورود یکپارچه به سیستم را فراهم می کند و کاربران شما را از به خاطر سپردن نام کاربری یا رمز عبور آزاد می کند.

کلیدهای عبور متکی به WebAuthn (تأیید هویت وب)، استانداردی است که به طور مشترک توسط اتحاد FIDO و کنسرسیوم وب جهانی (W3C) ایجاد شده است. WebAuthn از رمزنگاری کلید عمومی برای احراز هویت کاربر استفاده می کند. وب‌سایت یا برنامه‌ای که کاربر در حال ورود به آن است، می‌تواند کلید عمومی را ببیند و ذخیره کند، اما کلید خصوصی را هرگز. کلید خصوصی محرمانه و ایمن نگهداری می شود. و از آنجایی که کلید منحصربه‌فرد است و به وب‌سایت یا برنامه مرتبط است، کلیدهای عبور غیرقابل فیش کردن هستند و امنیت بیشتری را اضافه می‌کنند.

Credential Manager به کاربران اجازه می دهد تا کلیدهای عبور ایجاد کرده و آنها را در Google Password Manager ذخیره کنند.

احراز هویت کاربر با کلیدهای عبور را برای راهنمایی در مورد نحوه اجرای جریانهای احراز هویت یکپارچه کلید عبور با Credential Manager بخوانید.

پیش نیازها

برای استفاده از Credential Manager مراحل این بخش را کامل کنید.

از نسخه پلتفرم اخیر استفاده کنید

Credential Manager در Android 4.4 (سطح API 19) و بالاتر پشتیبانی می شود.

وابستگی ها را به برنامه خود اضافه کنید

وابستگی های زیر را به اسکریپت ساخت ماژول برنامه خود اضافه کنید:

کاتلین

dependencies {
    implementation("androidx.credentials:credentials:1.5.0-alpha05")

    // optional - needed for credentials support from play services, for devices running
    // Android 13 and below.
    implementation("androidx.credentials:credentials-play-services-auth:1.5.0-alpha05")
}

شیار

dependencies {
    implementation "androidx.credentials:credentials:1.5.0-alpha05"

    // optional - needed for credentials support from play services, for devices running
    // Android 13 and below.
    implementation "androidx.credentials:credentials-play-services-auth:1.5.0-alpha05"
}

حفظ کلاس ها در فایل ProGuard

در فایل proguard-rules.pro ماژول خود، دستورالعمل های زیر را اضافه کنید:

-if class androidx.credentials.CredentialManager
-keep class androidx.credentials.playservices.** {
  *;
}

درباره نحوه کوچک کردن، مبهم کردن و بهینه سازی برنامه خود بیشتر بیاموزید.

پشتیبانی از پیوندهای دارایی دیجیتال را اضافه کنید

برای فعال کردن پشتیبانی از کلید عبور برای برنامه Android خود، برنامه خود را با وب سایتی که برنامه شما متعلق به آن است مرتبط کنید. با انجام مراحل زیر می توانید این ارتباط را اعلام کنید:

  1. یک فایل JSON Links دارایی دیجیتال ایجاد کنید. برای مثال، برای اعلام اینکه وب‌سایت https://signin.example.com و یک برنامه Android با نام بسته com.example می‌توانند اعتبار ورود به سیستم را به اشتراک بگذارند، فایلی به نام assetlinks.json با محتوای زیر ایجاد کنید:

    [
      {
        "relation" : [
          "delegate_permission/common.handle_all_urls",
          "delegate_permission/common.get_login_creds"
        ],
        "target" : {
          "namespace" : "android_app",
          "package_name" : "com.example.android",
          "sha256_cert_fingerprints" : [
            SHA_HEX_VALUE
          ]
        }
      }
    ]
    

    فیلد relation آرایه ای از یک یا چند رشته است که رابطه اعلام شده را توصیف می کند. برای اعلام اینکه برنامه‌ها و سایت‌ها اعتبارنامه‌های ورود به سیستم را به اشتراک می‌گذارند، روابط را به‌عنوان delegate_permission/handle_all_urls و delegate_permission/common.get_login_creds مشخص کنید.

    فیلد target یک شی است که دارایی را مشخص می کند که اظهارنامه برای آن اعمال می شود. فیلدهای زیر یک وب سایت را مشخص می کند:

    namespace web
    site

    آدرس وب سایت، با فرمت https:// domain [: optional_port ] ; به عنوان مثال، https://www.example.com .

    domain باید کاملاً واجد شرایط باشد. و optional_port باید هنگام استفاده از پورت 443 برای HTTPS حذف شود.

    هدف site فقط می تواند یک دامنه ریشه باشد: شما نمی توانید یک انجمن برنامه را به یک زیر شاخه خاص محدود کنید. مسیری را در URL وارد نکنید، مانند یک اسلش انتهایی.

    دامنه‌های فرعی مطابق در نظر گرفته نمی‌شوند: یعنی اگر domain به عنوان www.example.com مشخص کنید، دامنه www.counter.example.com با برنامه شما مرتبط نیست.

    فیلدهای زیر یک برنامه اندروید را مشخص می کند:

    namespace android_app
    package_name نام بسته اعلام شده در مانیفست برنامه. برای مثال com.example.android
    sha256_cert_fingerprints اثر انگشت SHA256 گواهی امضای برنامه شما.
  2. فایل JSON Link Assets Digital را در مکان زیر در دامنه ورود میزبانی کنید:

    https://domain[:optional_port]/.well-known/assetlinks.json
    

    برای مثال، اگر دامنه ورود به سیستم شما signin.example.com است، فایل JSON را در https://signin.example.com/.well-known/assetlinks.json میزبانی کنید.

    نوع MIME برای فایل پیوند دارایی‌های دیجیتال باید JSON باشد. مطمئن شوید که سرور یک هدر Content-Type: application/json در پاسخ ارسال می کند.

  3. مطمئن شوید که میزبان شما به Google اجازه می‌دهد فایل پیوند دارایی دیجیتال شما را بازیابی کند. اگر فایل robots.txt دارید، باید به عامل Googlebot اجازه دهد /.well-known/assetlinks.json را بازیابی کند. اکثر سایت‌ها می‌توانند به هر عامل خودکار اجازه دهند فایل‌ها را در مسیر /.well-known/ بازیابی کند تا سایر سرویس‌ها بتوانند به ابرداده موجود در آن فایل‌ها دسترسی داشته باشند:

    User-agent: *
    Allow: /.well-known/
    
  4. خط زیر را به فایل مانیفست زیر <application> اضافه کنید:

    <meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
    
  5. اگر از ورود با رمز عبور از طریق Credential Manager استفاده می‌کنید، این مرحله را برای پیکربندی پیوند دارایی دیجیتال در مانیفست دنبال کنید. اگر فقط از کلیدهای عبور استفاده می کنید، این مرحله لازم نیست.

    ارتباط را در برنامه اندروید اعلام کنید. یک شی اضافه کنید که فایل‌های assetlinks.json را برای بارگیری مشخص می‌کند. شما باید از هرگونه آپستروف و علامت نقل قولی که در رشته استفاده می کنید فرار کنید. به عنوان مثال:

    <string name="asset_statements" translatable="false">
    [{
      \"include\": \"https://signin.example.com/.well-known/assetlinks.json\"
    }]
    </string>
    
    > GET /.well-known/assetlinks.json HTTP/1.1
    > User-Agent: curl/7.35.0
    > Host: signin.example.com
    
    < HTTP/1.1 200 OK
    < Content-Type: application/json
    

Credential Manager را پیکربندی کنید

برای پیکربندی و مقداردهی اولیه یک شی CredentialManager ، منطقی شبیه به زیر اضافه کنید:

کاتلین

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
val credentialManager = CredentialManager.create(context)

جاوا

// Use your app or activity context to instantiate a client instance of
// CredentialManager.
CredentialManager credentialManager = CredentialManager.create(context)

فیلدهای اعتبار را نشان دهید

در اندروید 14 و بالاتر، ویژگی isCredential را می توان برای نشان دادن فیلدهای اعتبار، مانند فیلدهای نام کاربری یا رمز عبور استفاده کرد. این ویژگی نشان می‌دهد که این نمای یک فیلد اعتبارنامه است که برای کار با مدیر اعتبار و ارائه‌دهندگان اعتبارنامه شخص ثالث در نظر گرفته شده است، در حالی که به خدمات تکمیل خودکار کمک می‌کند پیشنهادهای تکمیل خودکار بهتری ارائه دهند. وقتی برنامه از Credential Manager API استفاده می‌کند، صفحه پایینی Credential Manager با اعتبارنامه‌های موجود نمایش داده می‌شود و دیگر نیازی به نمایش کادر گفتگوی تکمیل خودکار برای نام کاربری یا رمز عبور نیست. به روشی مشابه، نیازی به نمایش کادر گفتگوی ذخیره خودکار تکمیل خودکار برای گذرواژه‌ها نیست، زیرا برنامه برای ذخیره اعتبارنامه از Credential Manager API درخواست می‌کند.

برای استفاده از ویژگی isCredential ، آن را به View های مربوطه اضافه کنید:

<TextView
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:isCredential="true"
...
 />

کاربر خود را وارد کنید

برای بازیابی همه گزینه‌های رمز عبور و رمز عبور مرتبط با حساب کاربر، این مراحل را کامل کنید:

  1. گزینه های احراز هویت رمز عبور و رمز عبور را راه اندازی کنید:

    کاتلین

    // Retrieves the user's saved password for your app from their
    // password provider.
    val getPasswordOption = GetPasswordOption()
    
    // Get passkey from the user's public key credential provider.
    val getPublicKeyCredentialOption = GetPublicKeyCredentialOption(
        requestJson = requestJson
    )

    جاوا

    // Retrieves the user's saved password for your app from their
    // password provider.
    GetPasswordOption getPasswordOption = new GetPasswordOption();
    
    // Get passkey from the user's public key credential provider.
    GetPublicKeyCredentialOption getPublicKeyCredentialOption =
            new GetPublicKeyCredentialOption(requestJson);
  2. از گزینه های بازیابی شده از مرحله قبل برای ایجاد درخواست ورود استفاده کنید.

    کاتلین

    val getCredRequest = GetCredentialRequest(
        listOf(getPasswordOption, getPublicKeyCredentialOption)
    )

    جاوا

    GetCredentialRequest getCredRequest = new GetCredentialRequest.Builder()
        .addCredentialOption(getPasswordOption)
        .addCredentialOption(getPublicKeyCredentialOption)
        .build();
  3. جریان ورود به سیستم را راه اندازی کنید:

    کاتلین

    coroutineScope.launch {
        try {
            val result = credentialManager.getCredential(
                // Use an activity-based context to avoid undefined system UI
                // launching behavior.
                context = activityContext,
                request = getCredRequest
            )
            handleSignIn(result)
        } catch (e : GetCredentialException) {
            handleFailure(e)
        }
    }
    
    fun handleSignIn(result: GetCredentialResponse) {
        // Handle the successfully returned credential.
        val credential = result.credential
    
        when (credential) {
            is PublicKeyCredential -> {
                val responseJson = credential.authenticationResponseJson
                // Share responseJson i.e. a GetCredentialResponse on your server to
                // validate and  authenticate
            }
            is PasswordCredential -> {
                val username = credential.id
                val password = credential.password
                // Use id and password to send to your server to validate
                // and authenticate
            }
          is CustomCredential -> {
              // If you are also using any external sign-in libraries, parse them
              // here with the utility functions provided.
              if (credential.type == ExampleCustomCredential.TYPE)  {
              try {
                  val ExampleCustomCredential = ExampleCustomCredential.createFrom(credential.data)
                  // Extract the required credentials and complete the authentication as per
                  // the federated sign in or any external sign in library flow
                  } catch (e: ExampleCustomCredential.ExampleCustomCredentialParsingException) {
                      // Unlikely to happen. If it does, you likely need to update the dependency
                      // version of your external sign-in library.
                      Log.e(TAG, "Failed to parse an ExampleCustomCredential", e)
                  }
              } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential")
              }
            } else -> {
                // Catch any unrecognized credential type here.
                Log.e(TAG, "Unexpected type of credential")
            }
        }
    }

    جاوا

    credentialManager.getCredentialAsync(
        // Use activity based context to avoid undefined
        // system UI launching behavior
        activity,
        getCredRequest,
        cancellationSignal,
        <executor>,
        new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
            @Override
            public void onResult(GetCredentialResponse result) {
                handleSignIn(result);
            }
    
            @Override
            public void onError(GetCredentialException e) {
                handleFailure(e);
            }
        }
    );
    
    public void handleSignIn(GetCredentialResponse result) {
        // Handle the successfully returned credential.
        Credential credential = result.getCredential();
        if (credential instanceof PublicKeyCredential) {
            String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
            // Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate
        } else if (credential instanceof PasswordCredential) {
            String username = ((PasswordCredential) credential).getId();
            String password = ((PasswordCredential) credential).getPassword();
            // Use id and password to send to your server to validate and authenticate
        } else if (credential instanceof CustomCredential) {
            if (ExampleCustomCredential.TYPE.equals(credential.getType())) {
                try {
                    ExampleCustomCredential customCred = ExampleCustomCredential.createFrom(customCredential.getData());
                    // Extract the required credentials and complete the
                    // authentication as per the federated sign in or any external
                    // sign in library flow
                } catch (ExampleCustomCredential.ExampleCustomCredentialParsingException e) {
                    // Unlikely to happen. If it does, you likely need to update the
                    // dependency version of your external sign-in library.
                    Log.e(TAG, "Failed to parse an ExampleCustomCredential", e);
                }
            } else {
                // Catch any unrecognized custom credential type here.
                Log.e(TAG, "Unexpected type of credential");
            }
        } else {
            // Catch any unrecognized credential type here.
            Log.e(TAG, "Unexpected type of credential");
        }
    }

مثال زیر نحوه فرمت کردن درخواست JSON را هنگام دریافت کلید عبور نشان می دهد:

{
  "challenge": "T1xCsnxM2DNL2KdK5CLa6fMhD7OBqho6syzInk_n-Uo",
  "allowCredentials": [],
  "timeout": 1800000,
  "userVerification": "required",
  "rpId": "credential-manager-app-test.glitch.me"
}

مثال زیر نشان می‌دهد که پاسخ JSON پس از دریافت اعتبار کلید عمومی چگونه ممکن است ظاهر شود:

{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uZ2V0IiwiY2hhbGxlbmdlIjoiVDF4Q3NueE0yRE5MMktkSzVDTGE2Zk1oRDdPQnFobzZzeXpJbmtfbi1VbyIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "authenticatorData": "j5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGQdAAAAAA",
    "signature": "MEUCIQCO1Cm4SA2xiG5FdKDHCJorueiS04wCsqHhiRDbbgITYAIgMKMFirgC2SSFmxrh7z9PzUqr0bK1HZ6Zn8vZVhETnyQ",
    "userHandle": "2HzoHm_hY0CjuEESY9tY6-3SdjmNHOoNqaPDcZGzsr0"
  }
}

وقتی هیچ اعتبارنامه ای در دسترس نیست، استثناها را مدیریت کنید

در برخی موارد، کاربر ممکن است هیچ اعتباری در دسترس نداشته باشد، یا ممکن است کاربر رضایت خود را با استفاده از یک اعتبار در دسترس ندهد. اگر getCredential() فراخوانی شود و هیچ اعتبارنامه ای پیدا نشود، یک NoCredentialException برگردانده می شود. اگر این اتفاق بیفتد، کد شما باید نمونه های NoCredentialException را مدیریت کند.

کاتلین

try {
  val credential = credentialManager.getCredential(credentialRequest)
} catch (e: NoCredentialException) {
  Log.e("CredentialManager", "No credential available", e)
}

جاوا

try {
  Credential credential = credentialManager.getCredential(credentialRequest);
} catch (NoCredentialException e) {
  Log.e("CredentialManager", "No credential available", e);
}

در اندروید 14 یا بالاتر، می‌توانید با استفاده از روش prepareGetCredential() قبل از فراخوانی getCredential() زمان تأخیر را هنگام نمایش انتخابگر حساب کاهش دهید.

کاتلین

val response = credentialManager.prepareGetCredential(
  GetCredentialRequest(
    listOf(
      <getPublicKeyCredentialOption>,
      <getPasswordOption>
    )
  )
}

جاوا

GetCredentialResponse response = credentialManager.prepareGetCredential(
  new GetCredentialRequest(
    Arrays.asList(
      new PublicKeyCredentialOption(),
      new PasswordOption()
    )
  )
);

متد prepareGetCredential() عناصر UI را فراخوانی نمی کند. این فقط به شما کمک می کند تا کار آماده سازی را انجام دهید تا بعداً بتوانید عملیات دریافت اعتبار باقیمانده (که شامل UI ها است) را از طریق getCredential() API اجرا کنید.

داده های کش در یک شی PrepareGetCredentialResponse برگردانده می شوند. اگر اعتبارنامه‌های موجود وجود داشته باشد، نتایج در حافظه پنهان ذخیره می‌شوند و بعداً می‌توانید API باقیمانده getCredential() را راه‌اندازی کنید تا انتخابگر حساب با داده‌های ذخیره‌شده نمایش داده شود.

جریان ثبت نام

شما می توانید یک کاربر را برای احراز هویت با استفاده از رمز عبور یا رمز عبور ثبت کنید.

یک رمز عبور ایجاد کنید

برای اینکه به کاربران این امکان را بدهید که رمز عبور را ثبت کنند و از آن برای احراز هویت مجدد استفاده کنند، یک اعتبار کاربری با استفاده از یک شی CreatePublicKeyCredentialRequest ثبت کنید.

کاتلین

fun createPasskey(requestJson: String, preferImmediatelyAvailableCredentials: Boolean) {
    val createPublicKeyCredentialRequest = CreatePublicKeyCredentialRequest(
        // Contains the request in JSON format. Uses the standard WebAuthn
        // web JSON spec.
        requestJson = requestJson,
        // Defines whether you prefer to use only immediately available
        // credentials, not hybrid credentials, to fulfill this request.
        // This value is false by default.
        preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials,
    )

    // Execute CreateCredentialRequest asynchronously to register credentials
    // for a user account. Handle success and failure cases with the result and
    // exceptions, respectively.
    coroutineScope.launch {
        try {
            val result = credentialManager.createCredential(
                // Use an activity-based context to avoid undefined system
                // UI launching behavior
                context = activityContext,
                request = createPublicKeyCredentialRequest,
            )
            handlePasskeyRegistrationResult(result)
        } catch (e : CreateCredentialException){
            handleFailure(e)
        }
    }
}

fun handleFailure(e: CreateCredentialException) {
    when (e) {
        is CreatePublicKeyCredentialDomException -> {
            // Handle the passkey DOM errors thrown according to the
            // WebAuthn spec.
            handlePasskeyError(e.domError)
        }
        is CreateCredentialCancellationException -> {
            // The user intentionally canceled the operation and chose not
            // to register the credential.
        }
        is CreateCredentialInterruptedException -> {
            // Retry-able error. Consider retrying the call.
        }
        is CreateCredentialProviderConfigurationException -> {
            // Your app is missing the provider configuration dependency.
            // Most likely, you're missing the
            // "credentials-play-services-auth" module.
        }
        is CreateCredentialUnknownException -> ...
        is CreateCredentialCustomException -> {
            // You have encountered an error from a 3rd-party SDK. If you
            // make the API call with a request object that's a subclass of
            // CreateCustomCredentialRequest using a 3rd-party SDK, then you
            // should check for any custom exception type constants within
            // that SDK to match with e.type. Otherwise, drop or log the
            // exception.
        }
        else -> Log.w(TAG, "Unexpected exception type ${e::class.java.name}")
    }
}

جاوا

public void createPasskey(String requestJson, boolean preferImmediatelyAvailableCredentials) {
    CreatePublicKeyCredentialRequest createPublicKeyCredentialRequest =
            // `requestJson` contains the request in JSON format. Uses the standard
            // WebAuthn web JSON spec.
            // `preferImmediatelyAvailableCredentials` defines whether you prefer
            // to only use immediately available credentials, not  hybrid credentials,
            // to fulfill this request. This value is false by default.
            new CreatePublicKeyCredentialRequest(
                requestJson, preferImmediatelyAvailableCredentials);

    // Execute CreateCredentialRequest asynchronously to register credentials
    // for a user account. Handle success and failure cases with the result and
    // exceptions, respectively.
    credentialManager.createCredentialAsync(
        // Use an activity-based context to avoid undefined system
        // UI launching behavior
        requireActivity(),
        createPublicKeyCredentialRequest,
        cancellationSignal,
        executor,
        new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
            @Override
            public void onResult(CreateCredentialResponse result) {
                handleSuccessfulCreatePasskeyResult(result);
            }

            @Override
            public void onError(CreateCredentialException e) {
                if (e instanceof CreatePublicKeyCredentialDomException) {
                    // Handle the passkey DOM errors thrown according to the
                    // WebAuthn spec.
                    handlePasskeyError(((CreatePublicKeyCredentialDomException)e).getDomError());
                } else if (e instanceof CreateCredentialCancellationException) {
                    // The user intentionally canceled the operation and chose not
                    // to register the credential.
                } else if (e instanceof CreateCredentialInterruptedException) {
                    // Retry-able error. Consider retrying the call.
                } else if (e instanceof CreateCredentialProviderConfigurationException) {
                    // Your app is missing the provider configuration dependency.
                    // Most likely, you're missing the
                    // "credentials-play-services-auth" module.
                } else if (e instanceof CreateCredentialUnknownException) {
                } else if (e instanceof CreateCredentialCustomException) {
                    // You have encountered an error from a 3rd-party SDK. If
                    // you make the API call with a request object that's a
                    // subclass of
                    // CreateCustomCredentialRequest using a 3rd-party SDK,
                    // then you should check for any custom exception type
                    // constants within that SDK to match with e.type.
                    // Otherwise, drop or log the exception.
                } else {
                  Log.w(TAG, "Unexpected exception type "
                          + e.getClass().getName());
                }
            }
        }
    );
}

درخواست JSON را فرمت کنید

پس از ایجاد رمز عبور، باید آن را با حساب کاربری مرتبط کنید و کلید عمومی کلید عبور را در سرور خود ذخیره کنید. مثال کد زیر نحوه فرمت کردن درخواست JSON را هنگام ایجاد یک رمز عبور نشان می دهد.

این پست وبلاگ در مورد آوردن احراز هویت یکپارچه به برنامه های شما به شما نشان می دهد که چگونه درخواست JSON خود را هنگام ایجاد کلیدهای عبور و زمانی که با استفاده از کلیدهای عبور احراز هویت می کنید، فرمت کنید. همچنین توضیح می‌دهد که چرا گذرواژه‌ها یک راه‌حل مؤثر برای احراز هویت نیستند، چگونه می‌توان از اعتبارنامه‌های بیومتریک موجود استفاده کرد، چگونه برنامه خود را با وب‌سایتی که مالک آن هستید مرتبط کنید، چگونه کلیدهای عبور ایجاد کنید، و چگونه با استفاده از کلیدهای عبور احراز هویت کنید.

{
  "challenge": "abc123",
  "rp": {
    "name": "Credential Manager example",
    "id": "credential-manager-test.example.com"
  },
  "user": {
    "id": "def456",
    "name": "helloandroid@gmail.com",
    "displayName": "helloandroid@gmail.com"
  },
  "pubKeyCredParams": [
    {
      "type": "public-key",
      "alg": -7
    },
    {
      "type": "public-key",
      "alg": -257
    }
  ],
  "timeout": 1800000,
  "attestation": "none",
  "excludeCredentials": [
    {"id": "ghi789", "type": "public-key"},
    {"id": "jkl012", "type": "public-key"}
  ],
  "authenticatorSelection": {
    "authenticatorAttachment": "platform",
    "requireResidentKey": true,
    "residentKey": "required",
    "userVerification": "required"
  }
}

مقادیری را برای authenticatorAttachment تنظیم کنید

پارامتر authenticatorAttachment را فقط می توان در زمان ایجاد اعتبار تنظیم کرد. می توانید platform ، cross-platform یا بدون مقدار را مشخص کنید. در بیشتر موارد، هیچ ارزشی توصیه نمی شود.

  • platform : برای ثبت دستگاه فعلی کاربر یا درخواست از کاربر پسورد برای ارتقا به کلیدهای عبور پس از ورود به سیستم، authenticatorAttachment روی platform تنظیم کنید.
  • cross-platform : این مقدار معمولاً هنگام ثبت اعتبار چند عاملی استفاده می شود و در زمینه رمز عبور استفاده نمی شود.
  • بدون ارزش : برای ارائه انعطاف‌پذیری به کاربران برای ایجاد کلیدهای عبور در دستگاه‌های دلخواهشان (مانند تنظیمات حساب)، پارامتر authenticatorAttachment نباید زمانی که کاربر انتخاب می‌کند یک رمز عبور اضافه کند، مشخص شود. در بیشتر موارد، نامشخص گذاشتن پارامتر بهترین گزینه است.

از ایجاد رمزهای عبور تکراری جلوگیری کنید

شناسه‌های اعتبار را در آرایه اختیاری excludeCredentials فهرست کنید تا از ایجاد یک رمز عبور جدید در صورتی که قبلاً با همان ارائه‌دهنده رمز عبور وجود داشته باشد، جلوگیری کنید.

پاسخ JSON را مدیریت کنید

قطعه کد زیر نمونه ای از پاسخ JSON را برای ایجاد اعتبار کلید عمومی نشان می دهد. درباره نحوه رسیدگی به اعتبار کلید عمومی بازگردانده شده بیشتر بیاموزید.

{
  "id": "KEDetxZcUfinhVi6Za5nZQ",
  "type": "public-key",
  "rawId": "KEDetxZcUfinhVi6Za5nZQ",
  "response": {
    "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoibmhrUVhmRTU5SmI5N1Z5eU5Ka3ZEaVh1Y01Fdmx0ZHV2Y3JEbUdyT0RIWSIsIm9yaWdpbiI6ImFuZHJvaWQ6YXBrLWtleS1oYXNoOk1MTHpEdll4UTRFS1R3QzZVNlpWVnJGUXRIOEdjVi0xZDQ0NEZLOUh2YUkiLCJhbmRyb2lkUGFja2FnZU5hbWUiOiJjb20uZ29vZ2xlLmNyZWRlbnRpYWxtYW5hZ2VyLnNhbXBsZSJ9",
    "attestationObject": "o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0YViUj5r_fLFhV-qdmGEwiukwD5E_5ama9g0hzXgN8thcFGRdAAAAAAAAAAAAAAAAAAAAAAAAAAAAEChA3rcWXFH4p4VYumWuZ2WlAQIDJiABIVgg4RqZaJyaC24Pf4tT-8ONIZ5_Elddf3dNotGOx81jj3siWCAWXS6Lz70hvC2g8hwoLllOwlsbYatNkO2uYFO-eJID6A"
  }
}

منشا را از داده های مشتری JSON تأیید کنید

origin برنامه یا وب‌سایتی را نشان می‌دهد که درخواست از آن می‌آید و توسط کلیدهای عبور برای محافظت در برابر حملات فیشینگ استفاده می‌شود. سرور برنامه شما باید مبدا داده های سرویس گیرنده را در برابر لیست مجاز برنامه ها و وب سایت های تأیید شده بررسی کند. اگر سرور درخواستی از یک برنامه یا وب سایت از یک منبع ناشناس دریافت کند، درخواست باید رد شود.

در مورد وب، origin منشا همان سایتی است که در آن اعتبارنامه وارد شده است. برای مثال، با توجه به URL https://www.example.com:8443/store?category=shoes#athletic ، origin https://www.example.com:8443 است. https://www.example.com:8443 .

برای برنامه‌های اندروید، عامل کاربر به‌طور خودکار origin برای امضای برنامه تماس تنظیم می‌کند. این امضا باید به عنوان یک تطابق در سرور شما تأیید شود تا تماس گیرنده API کلید عبور تأیید شود. origin Android یک URI است که از هش SHA-256 گواهی امضای APK مشتق شده است، مانند:

android:apk-key-hash:<sha256_hash-of-apk-signing-cert>

هش های SHA-256 گواهی های امضای یک فروشگاه کلید را می توان با اجرای دستور ترمینال زیر پیدا کرد:

keytool -list -keystore <path-to-apk-signing-keystore>

هش‌های SHA-256 در قالب هگزادسیمال محدود شده با کولون هستند ( 91:F7:CB:F9:D6:81… )، و مقادیر origin Android با کدگذاری مبنا ۶۴url هستند. این مثال پایتون نحوه تبدیل فرمت هش را به یک فرمت هگزادسیمال سازگار و جدا شده با کولون نشان می دهد:

import binascii
import base64
fingerprint = '91:F7:CB:F9:D6:81:53:1B:C7:A5:8F:B8:33:CC:A1:4D:AB:ED:E5:09:C5'
print("android:apk-key-hash:" + base64.urlsafe_b64encode(binascii.a2b_hex(fingerprint.replace(':', ''))).decode('utf8').replace('=', ''))

ارزش fingerprint را با مقدار خود جایگزین کنید. در اینجا یک نتیجه مثال است:

android:apk-key-hash:kffL-daBUxvHpY-4M8yhTavt5QnFEI2LsexohxrGPYU

سپس می توانید آن رشته را به عنوان مبدأ مجاز در سرور خود مطابقت دهید. اگر چندین گواهی امضا، مانند گواهی‌های اشکال‌زدایی و انتشار، یا چندین برنامه دارید، این فرآیند را تکرار کنید و همه آن مبداها را به عنوان معتبر در سرور بپذیرید.

رمز عبور کاربر را ذخیره کنید

اگر کاربر یک نام کاربری و رمز عبور برای یک جریان احراز هویت در برنامه شما ارائه دهد، می توانید یک اعتبار کاربری را ثبت کنید که می تواند برای احراز هویت کاربر استفاده شود. برای انجام این کار، یک شی CreatePasswordRequest ایجاد کنید:

کاتلین

fun registerPassword(username: String, password: String) {
    // Initialize a CreatePasswordRequest object.
    val createPasswordRequest =
            CreatePasswordRequest(id = username, password = password)

    // Create credential and handle result.
    coroutineScope.launch {
        try {
            val result =
                credentialManager.createCredential(
                    // Use an activity based context to avoid undefined
                    // system UI launching behavior.
                    activityContext,
                    createPasswordRequest
                  )
            handleRegisterPasswordResult(result)
        } catch (e: CreateCredentialException) {
            handleFailure(e)
        }
    }
}

جاوا

void registerPassword(String username, String password) {
    // Initialize a CreatePasswordRequest object.
    CreatePasswordRequest createPasswordRequest =
        new CreatePasswordRequest(username, password);

    // Register the username and password.
    credentialManager.createCredentialAsync(
        // Use an activity-based context to avoid undefined
        // system UI launching behavior
        requireActivity(),
        createPasswordRequest,
        cancellationSignal,
        executor,
        new CredentialManagerCallback<CreateCredentialResponse, CreateCredentialException>() {
            @Override
            public void onResult(CreateCredentialResponse result) {
                handleResult(result);
            }

            @Override
            public void onError(CreateCredentialException e) {
                handleFailure(e);
            }
        }
    );
}

پشتیبانی از بازیابی اعتبار

اگر کاربر دیگر به دستگاهی که اطلاعات کاربری خود را در آن ذخیره کرده است دسترسی نداشته باشد، ممکن است لازم باشد از یک نسخه پشتیبان آنلاین امن بازیابی کند. برای کسب اطلاعات بیشتر در مورد نحوه پشتیبانی از این فرآیند بازیابی اعتبار، بخش با عنوان «بازیابی دسترسی یا افزودن دستگاه‌های جدید» را در این پست وبلاگ بخوانید: امنیت کلیدهای عبور در Google Password Manager .

پشتیبانی از ابزارهای مدیریت رمز عبور را با URL های شناخته شده نقاط پایانی کلید عبور اضافه کنید

برای یکپارچه‌سازی یکپارچه و سازگاری در آینده با ابزارهای مدیریت رمز عبور و اعتبار، توصیه می‌کنیم برای URLهای شناخته شده نقاط پایانی کلید عبور، پشتیبانی اضافه کنید. این یک پروتکل باز برای طرف های هم تراز است تا به طور رسمی پشتیبانی خود را از کلیدهای عبور تبلیغ کنند و پیوندهای مستقیم برای ثبت نام و مدیریت کلید عبور ارائه دهند.

  1. برای یک طرف متکی در https://example.com ، که دارای یک وب سایت به علاوه برنامه های Android و iOS است، نشانی اینترنتی معروف https://example.com/.well-known/passkey-endpoints خواهد بود.
  2. هنگامی که URL مورد پرسش قرار می گیرد، پاسخ باید از طرح زیر استفاده کند

    {
      "enroll": "https://example.com/account/manage/passkeys/create"
      "manage": "https://example.com/account/manage/passkeys"
    }
    
  3. برای اینکه این پیوند مستقیماً در برنامه شما به جای وب باز شود، از پیوندهای برنامه Android استفاده کنید.

  4. جزئیات بیشتر را می توان در توضیح دهنده URL شناخته شده نقاط پایانی کلید عبور در GitHub یافت.

با نشان دادن اینکه کدام ارائه دهنده آنها را ایجاد کرده است، به کاربران کمک کنید رمز عبور خود را مدیریت کنند

یکی از چالش‌هایی که کاربران هنگام مدیریت کلیدهای عبور متعدد مرتبط با یک برنامه خاص با آن مواجه می‌شوند، شناسایی رمز عبور صحیح برای ویرایش یا حذف است. برای رفع این مشکل، توصیه می‌شود برنامه‌ها و وب‌سایت‌ها اطلاعات اضافی مانند ارائه‌دهنده‌ای که اعتبارنامه را ایجاد کرده، تاریخ ایجاد و آخرین تاریخ استفاده را در فهرست کلیدهای عبور در صفحه تنظیمات برنامه‌تان قرار دهند. اطلاعات ارائه‌دهنده با بررسی AAGUID مرتبط با رمز عبور مربوطه. AAGUID را می توان به عنوان بخشی از داده های احراز هویت یک رمز عبور یافت.

به عنوان مثال، اگر کاربری با استفاده از Google Password Manager روی یک دستگاه مجهز به اندروید یک کلید عبور ایجاد کند، سپس RP یک AAGUID دریافت می کند که چیزی شبیه به این است: "ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4". طرف متکی می‌تواند کلید عبور را در فهرست رمز عبور حاشیه‌نویسی کند تا نشان دهد که با استفاده از Google Password Manager ایجاد شده است.

برای نگاشت یک AAGUID به یک ارائه‌دهنده رمز عبور، RPها می‌توانند از یک مخزن منبع جامعه از AAGUID استفاده کنند. AAGUID را در لیست جستجو کنید تا نام و نماد ارائه دهنده کلید عبور را بیابید.

درباره ادغام AAGUID بیشتر بخوانید.

عیب یابی خطاهای رایج

جدول زیر چندین کد خطا و توضیحات رایج را نشان می‌دهد و اطلاعاتی در مورد علل آنها ارائه می‌کند:

کد خطا و توضیحات علت
در شروع ورود ناموفق: 16: تماس‌گیرنده به دلیل درخواست‌های ورود بیش از حد لغو شده به‌طور موقت مسدود شده است.

اگر در طول توسعه با این دوره خنک‌دانی 24 ساعته مواجه شدید، می‌توانید با پاک کردن فضای ذخیره‌سازی برنامه سرویس‌های Google Play، آن را بازنشانی کنید.

از طرف دیگر، برای جابجایی این خنک‌کردن در یک دستگاه آزمایشی یا شبیه‌ساز، به برنامه Dialer بروید و کد زیر را وارد کنید: *#*#66382723#*#* . برنامه Dialer همه ورودی‌ها را پاک می‌کند و ممکن است بسته شود، اما پیام تأییدی وجود ندارد.

On Begin Sign In Failure: 8: خطای داخلی ناشناخته.
  1. دستگاه به درستی با حساب Google تنظیم نشده است.
  2. رمز عبور JSON به اشتباه ایجاد می شود.
CreatePublicKeyCredentialDomException: درخواست ورودی قابل تایید نیست شناسه بسته برنامه در سرور شما ثبت نشده است. این را در ادغام سمت سرور خود تأیید کنید.
CreateCredentialUnknownException: در حین ذخیره رمز عبور، پاسخ شکست رمز عبور با یک ضربه پیدا شد. این خطا فقط در اندروید 13 و پایین‌تر رخ می‌دهد و تنها در صورتی که Google ارائه‌دهنده تکمیل خودکار باشد. در این حالت، کاربران یک درخواست ذخیره از تکمیل خودکار می بینند و رمز عبور در Google Password Manager ذخیره می شود. توجه داشته باشید که اعتبارنامه‌هایی که با استفاده از تکمیل خودکار با Google ذخیره می‌شوند، به‌صورت دو طرفه با Credential Manager API به اشتراک گذاشته می‌شوند. در نتیجه، این خطا را می توان با خیال راحت نادیده گرفت.

منابع اضافی

برای کسب اطلاعات بیشتر درباره Credential Manager API و کلیدهای عبور، منابع زیر را مشاهده کنید: