Credential Manager הוא Jetpack API שתומך בכניסה עם מספר חשבונות שיטות כמו שם משתמש וסיסמה, מפתחות גישה וכניסה מאוחדת. פתרונות (כמו 'כניסה באמצעות חשבון Google') בממשק API יחיד, וכך לפשט את עבור מפתחים.
בנוסף, Credential Manager מציע ממשק אחיד לכניסה לאפליקציות בכמה שיטות: 'כניסה באמצעות חשבון Google', מפתחות גישה וסיסמאות, שכולן יקלו על המשתמשים להיכנס לאפליקציות שונות.
בדף הזה נסביר את המושג של מפתחות גישה ואת השלבים להטמעה לתמיכה בצד הלקוח בפתרונות אימות, כולל מפתחות גישה, באמצעות Credential Manager API. יש גם דף שאלות נפוצות נפרד שבו מוצגות תשובות לשאלות מפורטות וספציפיות יותר.
המשוב שלכם הוא חלק חיוני בשיפור ה-Credential Manager API. אתם יכולים לשלוח לנו בעיות שמצאתם או רעיונות לשיפור ה-API באמצעות הקישור הבא:
מידע על מפתחות גישה
מפתחות גישה הם תחליף בטוח וקל יותר לסיסמאות. עם מפתחות גישה, יכולים להיכנס לאפליקציות ולאתרים באמצעות חיישן ביומטרי (כמו טביעת אצבע או זיהוי פנים), קוד אימות או קו ביטול נעילה. כך המשתמשים יכולים להיכנס לחשבון בקלות, בלי לזכור שמות משתמשים או סיסמאות.
מפתחות גישה מסתמכים על WebAuthn (אימות באינטרנט), פתרון סטנדרטי שפותח במשותף על ידי The FIDO Alliance ו-World Wide Web Consortium (W3C). ב-WebAuthn נעשה שימוש בקריפטוגרפיה של מפתח ציבורי כדי לאמת את המשתמש. האתר או האפליקציה שבהם המשתמש נכנס לחשבון יכולים לראות ולאחסן את המפתח הציבורי, אבל אף פעם לא את המפתח הפרטי. המפתח הפרטי נשמר בסודיות ובבטחה. בנוסף, מכיוון שהמפתח ייחודי ומקושר לאתר או לאפליקציה, אי אפשר לפרוץ למפתחות גישה, וכך הם מספקים עוד שכבת אבטחה.
Credential Manager מאפשר למשתמשים ליצור מפתחות גישה ולאחסן אותם במנהל הסיסמאות של Google.
במאמר אימות משתמשים באמצעות מפתחות גישה מוסבר איך להטמיע. תהליך אימות חלק של מפתח גישה באמצעות 'מנהל פרטי הכניסה'.
דרישות מוקדמות
כדי להשתמש ב-Credential Manager, מבצעים את השלבים שמפורטים בקטע הזה.
שימוש בגרסה עדכנית של הפלטפורמה
יש תמיכה ב-Credential Manager ב-Android 4.4 (רמת API 19) ואילך.
הוספת יחסי תלות לאפליקציה
צריך להוסיף את יחסי התלות הבאים לסקריפט ה-build של מודול האפליקציה:
Kotlin
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") }
Groovy
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.** {
*;
}
מידע נוסף על כיווץ, ערפול קוד (obfuscation) של האפליקציה ואופטימיזציה.
הוספת תמיכה בקישורים לנכסים דיגיטליים
כדי להפעיל תמיכה במפתחות גישה לאפליקציה ל-Android, צריך לשייך את האפליקציה אל אתר שבבעלות האפליקציה שלכם. כדי להצהיר על השיוך הזה, צריך למלא את את השלבים הבאים:
יוצרים קובץ JSON עם Digital Asset 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
כתובת ה-URL של האתר, בפורמט
https://domain[:optional_port]
. לדוגמה,https://www.example.com
.השדה domain חייב להיות מוגדר במלואו, ו-optional_port חייב להימחק כשמשתמשים ביציאה 443 ל-HTTPS.
יעד
site
יכול להיות רק דומיין ברמה הבסיסית: אי אפשר להגביל את השיוך של האפליקציה לתיקיית משנה ספציפית. אין לכלול נתיב בכתובת ה-URL, כמו קו נטוי בסוף.תת-דומיינים לא נחשבים תואמים: כלומר, אם תציינו את domain בתור
www.example.com
, הדומייןwww.counter.example.com
לא משויך לאפליקציה שלך.בשדות הבאים מזהים אפליקציה ל-Android:
namespace
android_app
package_name
שם החבילה שצוין במניפסט של האפליקציה. לדוגמה, com.example.android
sha256_cert_fingerprints
טביעות האצבעות מסוג SHA256 של האפליקציה חתימה על אישור. מארחים את קובץ ה-JSON של Digital Asset Links במיקום הבא בדומיין הכניסה:
https://domain[:optional_port]/.well-known/assetlinks.json
לדוגמה, אם הדומיין של כניסה הוא
signin.example.com
, מארחים את קובץ ה-JSON בכתובתhttps://signin.example.com/.well-known/assetlinks.json
.סוג ה-MIME של הקובץ Digital Asset Link צריך להיות JSON. כדאי לוודא השרת שולח בתגובה את הכותרת
Content-Type: application/json
.עליך לוודא שהמארח מאפשר ל-Google לאחזר את הקישור לנכס הדיגיטלי שלך חדש. אם יש לכם קובץ
robots.txt
, הוא חייב לאפשר לסוכן Googlebot: אחזור/.well-known/assetlinks.json
. רוב האתרים יכולים לתת הרשאה סוכן אוטומטי יאחזר קבצים בנתיב/.well-known/
כדי השירותים האלה יכולים לגשת למטא-נתונים בקבצים האלה:User-agent: * Allow: /.well-known/
מוסיפים את השורה הבאה לקובץ המניפסט בקטע
<application>
:<meta-data android:name="asset_statements" android:resource="@string/asset_statements" />
אם אתם משתמשים בכניסה באמצעות סיסמה דרך Credential Manager, עליכם לפעול לפי השלב הזה כדי להגדיר קישור של נכסים דיגיטליים במניפסט. אין צורך לבצע את השלב הזה אם אתם משתמשים רק במפתחות גישה.
מגדירים את השיוך באפליקציה ל-Android. מוסיפים אובייקט שמציין את קובצי
assetlinks.json
שרוצים לטעון. צריך לסמן בתו בריחה (escape) את כל הגרשיים המירכאות שבהן משתמשים במחרוזת. לדוגמה:<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
הגדרה של 'מנהל פרטי הכניסה'
כדי להגדיר ולאתחל אובייקט CredentialManager
, מוסיפים לוגיקה דומה לזו:
Kotlin
// Use your app or activity context to instantiate a client instance of // CredentialManager. val credentialManager = CredentialManager.create(context)
Java
// Use your app or activity context to instantiate a client instance of // CredentialManager. CredentialManager credentialManager = CredentialManager.create(context)
סימון השדות של פרטי הכניסה
ב-Android מגרסה 14 ואילך, אפשר להשתמש במאפיין isCredential
כדי:
לציין שדות של פרטי כניסה, כמו שדות של שם משתמש או סיסמה. המאפיין הזה מציין שהתצוגה הזו היא שדה פרטי כניסה שנועד לפעול עם מנהל פרטי הכניסה ועם ספקי פרטי כניסה של צד שלישי, ועוזרת לשירותי מילוי אוטומטי לספק הצעות טובות יותר למילוי אוטומטי. מתי האפליקציה משתמשת בפרטי הכניסה
Manager API, הגיליון התחתון של Credential Manager עם פרטי הכניסה הזמינים הוא
ואין צורך להציג את תיבת הדו-שיח של המילוי האוטומטי
שם משתמש או סיסמה. באופן דומה, אין צורך להציג
תיבת דו-שיח לשמירת סיסמאות, כי האפליקציה תבקש את ה-Credential Manager API
כדי לשמור את פרטי הכניסה.
כדי להשתמש במאפיין isCredential
, מוסיפים אותו לתצוגות הרלוונטיות:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:isCredential="true"
...
/>
כניסה של המשתמש
כדי לאחזר את כל האפשרויות של מפתחות הגישה והסיסמאות שמשויכות לחשבון המשתמש, מבצעים את השלבים הבאים:
מאתחלים את אפשרויות האימות של הסיסמה ומפתח הגישה:
Kotlin
// 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 )
Java
// 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);
משתמשים באפשרויות שאוחזרו מהשלב הקודם כדי ליצור את בקשת כניסה.
Kotlin
val getCredRequest = GetCredentialRequest( listOf(getPasswordOption, getPublicKeyCredentialOption) )
Java
GetCredentialRequest getCredRequest = new GetCredentialRequest.Builder() .addCredentialOption(getPasswordOption) .addCredentialOption(getPublicKeyCredentialOption) .build();
מפעילים את תהליך הכניסה:
Kotlin
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") } } }
Java
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
.
Kotlin
try {
val credential = credentialManager.getCredential(credentialRequest)
} catch (e: NoCredentialException) {
Log.e("CredentialManager", "No credential available", e)
}
Java
try {
Credential credential = credentialManager.getCredential(credentialRequest);
} catch (NoCredentialException e) {
Log.e("CredentialManager", "No credential available", e);
}
ב-Android מגרסה 14 ואילך, אפשר להשתמש בשיטה prepareGetCredential()
לפני שמפעילים את getCredential()
כדי לקצר את זמן האחזור כשמציגים את בורר החשבונות.
Kotlin
val response = credentialManager.prepareGetCredential(
GetCredentialRequest(
listOf(
<getPublicKeyCredentialOption>,
<getPasswordOption>
)
)
}
Java
GetCredentialResponse response = credentialManager.prepareGetCredential(
new GetCredentialRequest(
Arrays.asList(
new PublicKeyCredentialOption(),
new PasswordOption()
)
)
);
ה-method prepareGetCredential()
לא מפעילה רכיבי ממשק משתמש. הוא רק עוזר לכם לבצע את עבודת ההכנה כדי שתוכלו להפעיל מאוחר יותר את שאר הפעולות של get-credential (שכוללות ממשקי משתמש) דרך ה-API של getCredential()
.
הנתונים שנשמרו במטמון מוחזרים באובייקט PrepareGetCredentialResponse
. אם המיקום
ויש פרטי כניסה קיימים, התוצאות יישמרו במטמון,
מאוחר יותר להפעיל את ה-API הנותר של getCredential()
כדי להציג את החשבון
עם הנתונים השמורים במטמון.
תהליכי רישום
אפשר לרשום משתמש לאימות באמצעות מפתח גישה או סיסמה.
יצירת מפתח גישה
כדי לתת למשתמשים אפשרות לרשום מפתח גישה ולהשתמש בו לאימות מחדש, צריך לרשום את פרטי הכניסה של המשתמש באמצעות אובייקט CreatePublicKeyCredentialRequest
.
Kotlin
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}") } }
Java
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
.
באפליקציות ל-Android, סוכן המשתמש מגדיר באופן אוטומטי את 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 מקודדים בקידוד base64 URL.
הדוגמה הבאה ב-Python מדגימה איך להמיר את פורמט הגיבוב לפורמט תואם,
פורמט הקסדצימלי מופרד בנקודתיים:
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
:
Kotlin
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) } } }
Java
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.
הוספת תמיכה בכלים לניהול סיסמאות באמצעות כתובות URL מוכרות של נקודות קצה של מפתחות גישה
כדי לאפשר שילוב חלק ותאימות עתידית לכלים לניהול סיסמאות ופרטי כניסה, מומלץ להוסיף תמיכה ב-URLs ידועים של נקודות קצה של מפתחות גישה. זהו פרוטוקול פתוח שמאפשר לצדדים שתומכים במפתחות גישה לפרסם באופן רשמי את התמיכה שלהם במפתחות גישה ולספק קישורים ישירים להרשמה ולניהול של מפתחות גישה.
- לגורם נסמך ב-
https://example.com
, שכולל אתר ועוד באפליקציות ל-Android ול-iOS, כתובת ה-URL המוכרת תהיהhttps://example.com/.well-known/passkey-endpoints
. כששולחים שאילתה לגבי כתובת ה-URL, התגובה צריכה להשתמש בסכימה הבאה
{ "enroll": "https://example.com/account/manage/passkeys/create" "manage": "https://example.com/account/manage/passkeys" }
כדי שהקישור הזה ייפתח ישירות באפליקציה במקום באינטרנט, צריך להשתמש בקישורים לאפליקציות ל-Android.
פרטים נוספים זמינים הסבר על כתובת URL ידועה של נקודות קצה של מפתח גישה ב-GitHub.
אפשר לעזור למשתמשים לנהל את מפתחות הגישה שלהם על ידי הצגת הספק שיצר אותם
אחד האתגרים שהמשתמשים מתמודדים איתם כשהם מנהלים כמה מפתחות גישה שמשויכים לנכס מזהה את מפתח הגישה הנכון לעריכה או למחיקה. כדי לעזור לפתור את הבעיה הזו, מומלץ לאפליקציות ולאתרים לכלול מידע נוסף, כמו הספק שיצר את פרטי הכניסה, תאריך היצירה ותאריך השימוש האחרון, ברשימת מפתחות הגישה במסך ההגדרות של האפליקציה. כדי לקבל את פרטי הספק, בודקים את הערך של AAGUID שמשויך למפתח הגישה המתאים. אפשר למצוא את AAGUID כחלק ממפתח גישה נתונים ממאמת החשבונות.
לדוגמה, אם משתמש יוצר מפתח גישה במכשיר Android באמצעות מנהל הסיסמאות של Google, ה-RP מקבל AAGUID שנראה בערך כך: "ea9b8d66-4d01-1d21-3ce4-b6b48cb575d4". הצד הנסמך יכול להוסיף הערות למפתח הגישה ברשימת מפתחות הגישה כדי לציין שהוא נוצר באמצעות מנהל הסיסמאות של Google.
כדי למפות AAGUID לספק של מפתח גישה, גורמים מוגבלים יכולים להשתמש בקהילה שמבוססת על קהילה מאגר של AAGUID. חפשו את ה-AAGUID ב כדי למצוא את השם והסמל של ספק מפתח הגישה.
פתרון בעיות של שגיאות נפוצות
הטבלה הבאה מציגה כמה קודי שגיאה ותיאורים נפוצים, וגם מספק מידע על הסיבות לכך:
קוד השגיאה והתיאור שלו | הסיבה |
---|---|
ב-Begin Sign In Failure: 16: מבצע הקריאה חוסם באופן זמני בגלל יותר מדי הנחיות כניסה שבוטלו. | אם אתם נתקלים בתקופת צינון של 24 שעות במהלך הפיתוח, יכול לאפס אותו על ידי ניקוי Google Play Services אחסון האפליקציות. לחלופין, כדי להפעיל את ההפחתה ההדרגתית במכשיר בדיקה או באמולטור, עוברים
לאפליקציית החייגן ומזינים את הקוד הבא:
|
כשל בהתחלת הכניסה: 8: שגיאה פנימית לא ידועה. |
|
CreatePublicKeyCredentialDomהחרגה: לא ניתן לבצע את הבקשה הנכנסת מאומת | מזהה החבילה של האפליקציה לא רשום בשרת. אימות בשילוב בצד השרת. |
CreateCredentialUnknownException: During save password, found password Failure response from one tap 16: Skipping password saving since the user Is likely prompted with Android Autofill | השגיאה הזו מתרחשת רק ב-Android מגרסה 13 ומטה, ורק אם Google היא ספקית המילוי האוטומטי. במקרה כזה, המשתמשים יראו בקשה לשמירה ממילוי אוטומטי, והסיסמה תישמר ב'מנהל הסיסמאות של Google'. חשוב לדעת שפרטי הכניסה שנשמרו באמצעות המילוי האוטומטי של Google משותפים עם Credential Manager API בשני הכיוונים. לכן אפשר להתעלם מהשגיאה הזו. |
מקורות מידע נוספים
למידע נוסף על Credential Manager API ומפתחות גישה, תוכלו לעיין במקורות המידע הבאים:
- מדריך בנושא חוויית המשתמש במפתחות גישה
- סרטון: איך להפחית את ההסתמכות על סיסמאות באפליקציות ל-Android באמצעות מפתח גישה תמיכה
- Codelab: איך מפשטים את תהליכי האימות באמצעות Credential Manager API באפליקציות ל-Android
- אפליקציה לדוגמה: CredentialManager