אתם יכולים לאפשר למשתמשים שלכם לבצע אימות באמצעות Firebase באמצעות Apple ID שלהם, על ידי שימוש ב-Firebase SDK כדי לבצע את תהליך הכניסה של OAuth 2.0 מקצה לקצה.
לפני שמתחילים
כדי לאפשר למשתמשים להיכנס באמצעות אפל, קודם צריך להגדיר את הכניסה באמצעות אפל באתר למפתחים של אפל, ואז להפעיל את אפל כספק כניסה בפרויקט Firebase.
הצטרפות לתוכנית המפתחים של אפל
רק חברים בתוכנית המפתחים של Apple יכולים להגדיר כניסה באמצעות Apple.
הגדרת כניסה באמצעות Apple
באתר Apple Developer, מבצעים את הפעולות הבאות:
-
משייכים את האתר לאפליקציה כמו שמתואר בקטע הראשון של המאמר בנושא הגדרת כניסה באמצעות Apple לאתרים. כשמופיעה הבקשה, רושמים את כתובת ה-URL הבאה ככתובת URL להחזרה:
https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler
מזהה הפרויקט ב-Firebase מופיע בכרטיסייה General (כללי) בקטע
Settings (הגדרות) במסוף Firebase.בסיום, חשוב לשים לב למזהה השירות החדש, שתצטרכו לו בקטע הבא.
- יוצרים מפתח פרטי של 'כניסה באמצעות אפל'. בקטע הבא תצטרכו את המפתח הפרטי החדש ואת מזהה המפתח.
-
אם אתם משתמשים בתכונות של Firebase Authentication ששולחות אימיילים למשתמשים, כולל כניסה באמצעות קישור באימייל, אימות כתובת אימייל, ביטול שינוי בחשבון ועוד, עליכם להגדיר את שירות ממסר האימייל הפרטי של אפל ולרשום את
noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com(או את הדומיין של תבנית האימייל המותאמת אישית שלכם) כדי שאפל תוכל להעביר אימיילים שנשלחים על ידי Firebase Authentication לכתובות אימייל אנונימיות של אפל.
הפעלת Apple כספק כניסה
- איך מוסיפים את Firebase לפרויקט Android
-
אם עדיין לא עשיתם זאת, מציינים את טביעת האצבע מסוג SHA-1 של האפליקציה.
-
במסוף Firebase, עוברים אל
הגדרות > הכרטיסייה כללי. - גוללים למטה לכרטיס האפליקציות שלך, בוחרים את האפליקציה ל-Android ומוסיפים את טביעת האצבע של אישור SHA-1 בשדה טביעות אצבע של אישור SHA.
-
במסוף Firebase, עוברים אל
- במסוף Firebase, עוברים אל Security (אבטחה) > Authentication (אימות).
- בכרטיסייה שיטת כניסה, מפעילים את ספק הכניסה אפל. מציינים את מזהה השירות שיצרתם בקטע הקודם. בנוסף, בקטע OAuth code flow configuration section, מציינים את מזהה הצוות שלכם ב-Apple, את המפתח הפרטי ואת מזהה המפתח שיצרתם בקטע הקודם.
עמידה בדרישות של Apple לגבי נתונים שעברו אנונימיזציה
התחברות באמצעות אפל מאפשרת למשתמשים להפוך את הנתונים שלהם לאנונימיים, כולל כתובת האימייל, כשהם מתחברים. משתמשים שבוחרים באפשרות הזו מקבלים כתובות אימייל עם הדומיין privaterelay.appleid.com. כשמשתמשים בתכונה 'כניסה באמצעות Apple' באפליקציה, צריך לפעול בהתאם לכללי המדיניות או לתנאים הרלוונטיים למפתחים של Apple בנוגע למזהי Apple האנונימיים האלה.
הדבר כולל קבלת הסכמה מהמשתמש הנדרשת לפני שמשייכים פרטים אישיים מזהים ישירות למזהה Apple אנונימי. כשמשתמשים באימות ב-Firebase, הפעולות האלה עשויות לכלול את הפעולות הבאות:
- לקשר כתובת אימייל ל-Apple ID שעבר אנונימיזציה או להיפך.
- קישור של מספר טלפון ל-Apple ID אנונימי או להיפך
- לקשר פרטי כניסה לרשתות חברתיות שלא עברו אנונימיזציה (פייסבוק, Google וכו') למזהה Apple שעבר אנונימיזציה או להיפך.
הרשימה שלמעלה היא חלקית. כדי לוודא שהאפליקציה עומדת בדרישות של Apple, אפשר לעיין בהסכם הרישיון של Apple Developer Program בקטע Membership בחשבון הפיתוח.
טיפול בתהליך הכניסה באמצעות Firebase SDK
ב-Android, הדרך הכי קלה לאמת את המשתמשים ב-Firebase באמצעות חשבונות Apple שלהם היא לטפל בתהליך הכניסה כולו באמצעות Firebase Android SDK.
כדי לטפל בתהליך הכניסה באמצעות Firebase Android SDK, פועלים לפי השלבים הבאים:
יוצרים מופע של
OAuthProviderבאמצעות Builder עם מזהה הספקapple.com:Kotlin
val provider = OAuthProvider.newBuilder("apple.com")Java
OAuthProvider.Builder provider = OAuthProvider.newBuilder("apple.com");אופציונלי: אפשר לציין היקפי הרשאות נוספים של OAuth 2.0 מעבר לברירת המחדל שרוצים לבקש מספק האימות.
Kotlin
provider.setScopes(arrayOf("email", "name"))Java
List<String> scopes = new ArrayList<String>() { { add("email"); add("name"); } }; provider.setScopes(scopes);כברירת מחדל, כשהאפשרות חשבון אחד לכל כתובת אימייל מופעלת, Firebase מבקש הרשאות גישה לכתובת האימייל ולשם. אם תשנו את ההגדרה הזו לכמה חשבונות לכל כתובת אימייל, מערכת Firebase לא תבקש הרשאות מ-Apple אלא אם תציינו אותן.
אופציונלי: אם רוצים להציג את מסך הכניסה של אפל בשפה שאינה אנגלית, מגדירים את הפרמטר
locale. במסמכים בנושא 'כניסה באמצעות Apple' מפורטים הלוקאלים הנתמכים.Kotlin
// Localize the Apple authentication screen in French. provider.addCustomParameter("locale", "fr")Java
// Localize the Apple authentication screen in French. provider.addCustomParameter("locale", "fr");מתבצע אימות ב-Firebase באמצעות אובייקט ספק OAuth. שימו לב שבניגוד לפעולות אחרות של
FirebaseAuth, הפעולה הזו תשתלט על ממשק המשתמש על ידי פתיחת כרטיסייה מותאמת אישית ב-Chrome. לכן, אל תפנו לפעילות שלכם ב-OnSuccessListenerוב-OnFailureListenerשאתם מצרפים, כי הם ינותקו מיד כשהפעולה תתחיל את ממשק המשתמש.קודם צריך לבדוק אם כבר קיבלת תשובה. אם נכנסים לחשבון בשיטה הזו, הפעילות עוברת לרקע, כלומר המערכת יכולה להחזיר אותה במהלך תהליך הכניסה. כדי לוודא שלא תבקשו מהמשתמש לנסות שוב אם זה יקרה, כדאי לבדוק אם כבר יש תוצאה.
כדי לבדוק אם יש תוצאה בהמתנה, מתקשרים למספר
getPendingAuthResult():Kotlin
val pending = auth.pendingAuthResult if (pending != null) { pending.addOnSuccessListener { authResult -> Log.d(TAG, "checkPending:onSuccess:$authResult") // Get the user profile with authResult.getUser() and // authResult.getAdditionalUserInfo(), and the ID // token from Apple with authResult.getCredential(). }.addOnFailureListener { e -> Log.w(TAG, "checkPending:onFailure", e) } } else { Log.d(TAG, "pending: null") }Java
mAuth = FirebaseAuth.getInstance(); Task<AuthResult> pending = mAuth.getPendingAuthResult(); if (pending != null) { pending.addOnSuccessListener(new OnSuccessListener<AuthResult>() { @Override public void onSuccess(AuthResult authResult) { Log.d(TAG, "checkPending:onSuccess:" + authResult); // Get the user profile with authResult.getUser() and // authResult.getAdditionalUserInfo(), and the ID // token from Apple with authResult.getCredential(). } }).addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Log.w(TAG, "checkPending:onFailure", e); } }); } else { Log.d(TAG, "pending: null"); }אם אין תוצאה בהמתנה, מתחילים את תהליך הכניסה על ידי קריאה ל-
startActivityForSignInWithProvider():Kotlin
auth.startActivityForSignInWithProvider(this, provider.build()) .addOnSuccessListener { authResult -> // Sign-in successful! Log.d(TAG, "activitySignIn:onSuccess:${authResult.user}") val user = authResult.user // ... } .addOnFailureListener { e -> Log.w(TAG, "activitySignIn:onFailure", e) }Java
mAuth.startActivityForSignInWithProvider(this, provider.build()) .addOnSuccessListener( new OnSuccessListener<AuthResult>() { @Override public void onSuccess(AuthResult authResult) { // Sign-in successful! Log.d(TAG, "activitySignIn:onSuccess:" + authResult.getUser()); FirebaseUser user = authResult.getUser(); // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Log.w(TAG, "activitySignIn:onFailure", e); } });בניגוד לספקים אחרים שנתמכים על ידי Firebase Auth, Apple לא מספקת כתובת URL של תמונה.
בנוסף, אם המשתמש בוחר לא לשתף את כתובת האימייל שלו עם האפליקציה, אפל מספקת כתובת אימייל ייחודית למשתמש (בפורמט
xyz@privaterelay.appleid.com) ומשתפת אותה עם האפליקציה שלכם. אם הגדרתם את שירות העברת האימייל הפרטי, אפל מעבירה אימיילים שנשלחים לכתובת האנונימית לכתובת האימייל האמיתית של המשתמש.Apple משתפת מידע על משתמשים, כמו השם לתצוגה, רק עם אפליקציות שמשתמש נכנס אליהן בפעם הראשונה. בדרך כלל, Firebase שומר את שם התצוגה בפעם הראשונה שמשתמש נכנס לחשבון באמצעות Apple, ואפשר לקבל אותו באמצעות
getCurrentUser().getDisplayName(). עם זאת, אם השתמשתם בעבר ב-Apple כדי להחתים משתמש באפליקציה בלי להשתמש ב-Firebase, Apple לא תספק ל-Firebase את השם המוצג של המשתמש.
אימות מחדש וקישור חשבונות
אפשר להשתמש באותו דפוס עם startActivityForReauthenticateWithProvider()
כדי לאחזר פרטי כניסה חדשים לפעולות רגישות שדורשות כניסה עדכנית:
Kotlin
// The user is already signed-in.
val firebaseUser = auth.getCurrentUser()
firebaseUser
.startActivityForReauthenticateWithProvider(/* activity= */ this, provider.build())
.addOnSuccessListener( authResult -> {
// User is re-authenticated with fresh tokens and
// should be able to perform sensitive operations
// like account deletion and email or password
// update.
})
.addOnFailureListener( e -> {
// Handle failure.
})
Java
// The user is already signed-in.
FirebaseUser firebaseUser = mAuth.getCurrentUser();
firebaseUser
.startActivityForReauthenticateWithProvider(/* activity= */ this, provider.build())
.addOnSuccessListener(
new OnSuccessListener<AuthResult>() {
@Override
public void onSuccess(AuthResult authResult) {
// User is re-authenticated with fresh tokens and
// should be able to perform sensitive operations
// like account deletion and email or password
// update.
}
})
.addOnFailureListener(
new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// Handle failure.
}
});
בנוסף, אפשר להשתמש ב-linkWithCredential() כדי לקשר ספקי זהויות שונים לחשבונות קיימים.
חשוב לזכור שחברת אפל דורשת לקבל הסכמה מפורשת מהמשתמשים לפני שמקשרים את החשבונות שלהם ב-Apple לנתונים אחרים.
לדוגמה, כדי לקשר חשבון פייסבוק לחשבון Firebase הנוכחי, משתמשים באסימון הגישה שקיבלתם מהתחברות המשתמש לפייסבוק:
Kotlin
// Initialize a Facebook credential with a Facebook access token.
val credential = FacebookAuthProvider.getCredential(token.getToken())
// Assuming the current user is an Apple user linking a Facebook provider.
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, task -> {
if (task.isSuccessful()) {
// Facebook credential is linked to the current Apple user.
// The user can now sign in to the same account
// with either Apple or Facebook.
}
});
Java
// Initialize a Facebook credential with a Facebook access token.
AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
// Assuming the current user is an Apple user linking a Facebook provider.
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Facebook credential is linked to the current Apple user.
// The user can now sign in to the same account
// with either Apple or Facebook.
}
}
});
למתקדמים: טיפול בתהליך הכניסה באופן ידני
אפשר גם לבצע אימות ב-Firebase באמצעות חשבון אפל. כדי לעשות זאת, צריך לטפל בתהליך הכניסה באמצעות Apple Sign-In JS SDK, לבנות ידנית את תהליך OAuth או להשתמש בספריית OAuth כמו AppAuth.
לכל בקשת כניסה, יוצרים מחרוזת אקראית – nonce – שבה משתמשים כדי לוודא שטוקן הזהות שמתקבל הוענק בתגובה ספציפית לבקשת האימות של האפליקציה. השלב הזה חשוב כדי למנוע התקפות שליחה מחדש.
אפשר ליצור ערך חד-פעמי מאובטח מבחינה קריפטוגרפית ב-Android באמצעות
SecureRandom, כמו בדוגמה הבאה:Kotlin
private fun generateNonce(length: Int): String { val generator = SecureRandom() val charsetDecoder = StandardCharsets.US_ASCII.newDecoder() charsetDecoder.onUnmappableCharacter(CodingErrorAction.IGNORE) charsetDecoder.onMalformedInput(CodingErrorAction.IGNORE) val bytes = ByteArray(length) val inBuffer = ByteBuffer.wrap(bytes) val outBuffer = CharBuffer.allocate(length) while (outBuffer.hasRemaining()) { generator.nextBytes(bytes) inBuffer.rewind() charsetDecoder.reset() charsetDecoder.decode(inBuffer, outBuffer, false) } outBuffer.flip() return outBuffer.toString() }Java
private String generateNonce(int length) { SecureRandom generator = new SecureRandom(); CharsetDecoder charsetDecoder = StandardCharsets.US_ASCII.newDecoder(); charsetDecoder.onUnmappableCharacter(CodingErrorAction.IGNORE); charsetDecoder.onMalformedInput(CodingErrorAction.IGNORE); byte[] bytes = new byte[length]; ByteBuffer inBuffer = ByteBuffer.wrap(bytes); CharBuffer outBuffer = CharBuffer.allocate(length); while (outBuffer.hasRemaining()) { generator.nextBytes(bytes); inBuffer.rewind(); charsetDecoder.reset(); charsetDecoder.decode(inBuffer, outBuffer, false); } outBuffer.flip(); return outBuffer.toString(); }לאחר מכן, מקבלים את גיבוב SHA256 של ה-nonce כמחרוזת הקסדצימלית:
Kotlin
private fun sha256(s: String): String { val md = MessageDigest.getInstance("SHA-256") val digest = md.digest(s.toByteArray()) val hash = StringBuilder() for (c in digest) { hash.append(String.format("%02x", c)) } return hash.toString() }Java
private String sha256(String s) throws NoSuchAlgorithmException { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digest = md.digest(s.getBytes()); StringBuilder hash = new StringBuilder(); for (byte c: digest) { hash.append(String.format("%02x", c)); } return hash.toString(); }תשלחו את גיבוב SHA256 של הערך החד-פעמי עם בקשת הכניסה, ו-Apple תעביר אותו ללא שינוי בתגובה. מערכת Firebase מאמתת את התגובה על ידי גיבוב של ה-nonce המקורי והשוואה שלו לערך שמועבר על ידי Apple.
מפעילים את תהליך הכניסה של אפל באמצעות ספריית OAuth או שיטה אחרת. חשוב לכלול את ה-nonce המגובב כפרמטר בבקשה.
אחרי שמקבלים את התשובה של אפל, מקבלים את טוקן הזהות מהתשובה ומשתמשים בו ובערך ה-nonce שלא עבר גיבוב כדי ליצור
AuthCredential:Kotlin
val credential = OAuthProvider.newCredentialBuilder("apple.com") .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce) .build()Java
AuthCredential credential = OAuthProvider.newCredentialBuilder("apple.com") .setIdTokenWithRawNonce(appleIdToken, rawUnhashedNonce) .build();אימות באמצעות Firebase באמצעות פרטי הכניסה של Firebase:
Kotlin
auth.signInWithCredential(credential) .addOnCompleteListener(this) { task -> if (task.isSuccessful) { // User successfully signed in with Apple ID token. // ... } }Java
mAuth.signInWithCredential(credential) .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() { @Override public void onComplete(@NonNull Task<AuthResult> task) { if (task.isSuccessful()) { // User successfully signed in with Apple ID token. // ... } } });
אם הקריאה אל signInWithCredential מצליחה, אפשר להשתמש בשיטה getCurrentUser
כדי לקבל את נתוני החשבון של המשתמש.
ביטול טוקן
Apple דורשת שאפליקציות שתומכות ביצירת חשבון יאפשרו למשתמשים להתחיל את תהליך מחיקת החשבון שלהם בתוך האפליקציה, כפי שמתואר בהנחיות לבדיקת אפליקציות ב-App Store.
בנוסף, באפליקציות שתומכות בכניסה באמצעות אפל, צריך להשתמש ב-API בארכיטקטורת REST של 'כניסה באמצעות אפל' כדי לבטל את הטוקנים של המשתמשים.
כדי לעמוד בדרישה הזו, צריך לבצע את השלבים הבאים:
משתמשים בשיטה
startActivityForSignInWithProvider()כדי להיכנס באמצעות Apple ולקבלAuthResult.מקבלים את טוקן הגישה של ספק Apple.
Kotlin
val oauthCredential: OAuthCredential = authResult.credential val accessToken = oauthCredential.accessTokenJava
OAuthCredential oauthCredential = (OAuthCredential) authResult.getCredential(); String accessToken = oauthCredential.getAccessToken();מבטלים את הטוקן באמצעות
revokeAccessTokenAPI.Kotlin
mAuth.revokeAccessToken(accessToken) .addOnCompleteListener(this) { task -> if (task.isSuccessful) { // Access token successfully revoked // for the user ... } }Java
mAuth.revokeAccessToken(accessToken) .addOnCompleteListener(this, new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { if (task.isSuccessful()) { // Access token successfully revoked // for the user ... } } });
- לבסוף, מוחקים את חשבון המשתמש (ואת כל הנתונים שמשויכים אליו).
השלבים הבאים
אחרי שמשתמש נכנס לחשבון בפעם הראשונה, נוצר חשבון משתמש חדש שמקושר לפרטי הכניסה – כלומר, שם המשתמש והסיסמה, מספר הטלפון או פרטי ספק האימות – שבאמצעותם המשתמש נכנס לחשבון. החשבון החדש הזה נשמר כחלק מפרויקט Firebase, ואפשר להשתמש בו כדי לזהות משתמש בכל האפליקציות בפרויקט, בלי קשר לשיטת הכניסה של המשתמש.
-
באפליקציות, אפשר לקבל את פרטי הפרופיל הבסיסיים של המשתמש מאובייקט
FirebaseUser. ניהול משתמשים ב-Firebase Realtime Database וב-Cloud Storage Security Rules, אפשר לקבל את מזהה המשתמש הייחודי של המשתמש המחובר מהמשתנה
auth, ולהשתמש בו כדי לקבוע לאילו נתונים משתמש יכול לגשת.
אתם יכולים לאפשר למשתמשים להיכנס לאפליקציה שלכם באמצעות כמה ספקי אימות על ידי קישור פרטי כניסה של ספק אימות לחשבון משתמש קיים.
כדי להוציא משתמש מהחשבון, מתקשרים אל
signOut:Kotlin
Firebase.auth.signOut()
Java
FirebaseAuth.getInstance().signOut();
-