אימות באמצעות Apple באמצעות JavaScript

אתם יכולים לאפשר למשתמשים לבצע אימות ב-Firebase באמצעות Apple ID שלהם, על ידי שימוש ב-Firebase SDK כדי לבצע את תהליך הכניסה של OAuth 2.0 מקצה לקצה.

לפני שמתחילים

כדי לאפשר למשתמשים להיכנס באמצעות Apple, קודם צריך להגדיר את הכניסה באמצעות Apple באתר למפתחים של Apple, ואז להפעיל את Apple כספק כניסה לפרויקט ב-Firebase.

הצטרפות לתוכנית המפתחים של Apple

רק חברים בתוכנית המפתחים של Apple יכולים להגדיר כניסה באמצעות Apple.

הגדרת כניסה באמצעות Apple

באתר Apple Developer, מבצעים את הפעולות הבאות:

  1. משייכים את האתר לאפליקציה כפי שמתואר בקטע הראשון של המאמר הגדרת 'כניסה באמצעות Apple' לאינטרנט. כשמוצגת בקשה, רושמים את כתובת ה-URL הבאה ככתובת URL להחזרה:

    https://YOUR_FIREBASE_PROJECT_ID.firebaseapp.com/__/auth/handler

    אפשר למצוא את מזהה הפרויקט ב-Firebase בדף הגדרות המסוף Firebase.

    בסיום, שימו לב למזהה השירות החדש, שתצטרכו אותו בקטע הבא.

  2. יצירת מפתח פרטי לכניסה באמצעות Apple תצטרכו את המפתח הפרטי החדש ואת מזהה המפתח בקטע הבא.
  3. אם אתם משתמשים באחת מהתכונות של Firebase Authentication ששולחות אימיילים למשתמשים, כולל כניסה באמצעות קישור לאימייל, אימות כתובת אימייל, ביטול שינוי חשבון ועוד, עליכם להגדיר את שירות העברת האימייל הפרטי של Apple ולרשום את noreply@YOUR_FIREBASE_PROJECT_ID.firebaseapp.com (או את הדומיין של תבנית האימייל בהתאמה אישית) כדי ש-Apple תוכל להעביר אימיילים שנשלחים על ידי Firebase Authentication לכתובות אימייל אנונימיות של Apple.

הפעלת Apple כספק כניסה

  1. מוסיפים את Firebase לפרויקט.
  2. במסוף Firebase, פותחים את הקטע Auth. בכרטיסייה Sign in method מפעילים את הספק Apple. מציינים את מזהה השירות שיצרתם בקטע הקודם. בנוסף, בקטע OAuth code flow configuration, מציינים את מזהה הצוות שלכם ב-Apple ואת המפתח הפרטי ומזהה המפתח שיצרתם בקטע הקודם.

עמידה בדרישות של Apple לגבי נתונים שעברו אנונימיזציה

כשמשתמשים ב'כניסה באמצעות חשבון Apple', הם יכולים להפוך את הנתונים שלהם, כולל כתובת האימייל, לאנונימיים בזמן הכניסה. למשתמשים שבוחרים באפשרות הזו יהיו כתובות אימייל עם הדומיין privaterelay.appleid.com. כשמשתמשים ב'כניסה באמצעות חשבון Apple' באפליקציה, צריך לפעול בהתאם לכללי המדיניות או לתנאים הרלוונטיים של Apple למפתחים בנוגע למזהי Apple האנונימיים האלה.

המשמעות היא שעליכם לקבל את הסכמת המשתמש הנדרשת לפני שתשייכו פרטים אישיים מזהים באופן ישיר למזהה Apple אנונימי. כשמשתמשים באימות ב-Firebase, הפעולות האלה עשויות לכלול:

  • קישור כתובת אימייל ל-Apple ID שעברה אנונימיזציה או להפך.
  • קישור של מספר טלפון ל-Apple ID שהוסרה ממנו הפרטיות או להפך
  • לקשר פרטי כניסה לא אנונימיים ברשתות חברתיות (Facebook, ‏ Google וכו') ל-Apple ID אנונימי, ולהפך.

הרשימה שלמעלה היא חלקית בלבד. כדי לוודא שהאפליקציה עומדת בדרישות של Apple, כדאי לעיין בהסכם הרישיון של תוכנית המפתחים של Apple בקטע 'מינוי' בחשבון הפיתוח.

טיפול בתהליך הכניסה באמצעות Firebase SDK

אם אתם מפתחים אפליקציית אינטרנט, הדרך הקלה ביותר לאמת את המשתמשים ב-Firebase באמצעות חשבונות Apple שלהם היא לטפל בכל תהליך הכניסה באמצעות Firebase JavaScript SDK.

כדי לטפל בתהליך הכניסה באמצעות Firebase JavaScript SDK:

  1. יוצרים מופע של OAuthProvider באמצעות מזהה הספק התואם apple.com.

    Web

    import { OAuthProvider } from "firebase/auth";
    
    const provider = new OAuthProvider('apple.com');

    Web

    var provider = new firebase.auth.OAuthProvider('apple.com');
  2. אופציונלי: מציינים היקפי הרשאות נוספים של OAuth 2.0 מעבר להיקפי ברירת המחדל שרוצים לבקש מספק האימות.

    Web

    provider.addScope('email');
    provider.addScope('name');

    Web

    provider.addScope('email');
    provider.addScope('name');

    כברירת מחדל, כשהאפשרות חשבון אחד לכל כתובת אימייל מופעלת, מערכת Firebase מבקשת הרשאות גישה לשם ולכתובת האימייל. אם משנים את ההגדרה הזו לאפשרות Multiple accounts per email address, מערכת Firebase לא מבקשת היקפי גישה מ-Apple אלא אם מציינים אותם.

  3. אופציונלי: אם רוצים להציג את מסך הכניסה של Apple בשפה שאינה אנגלית, מגדירים את הפרמטר locale. תוכלו למצוא את המיקומים הנתמכים במאמר כניסה באמצעות חשבון Apple.

    Web

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });

    Web

    provider.setCustomParameters({
      // Localize the Apple authentication screen in French.
      locale: 'fr'
    });
  4. אימות באמצעות Firebase באמצעות אובייקט הספק של OAuth. אתם יכולים לבקש מהמשתמשים להיכנס באמצעות חשבונות Apple שלהם על ידי פתיחת חלון קופץ או על ידי הפניה לדף הכניסה. השיטה להפניה אוטומטית היא המועדפת במכשירים ניידים.

    • כדי להיכנס באמצעות חלון קופץ, צריך לבצע את הפעולות הבאות:signInWithPopup()

      Web

      import { getAuth, signInWithPopup, OAuthProvider } from "firebase/auth";
      
      const auth = getAuth();
      signInWithPopup(auth, provider)
        .then((result) => {
          // The signed-in user info.
          const user = result.user;
      
          // Apple credential
          const credential = OAuthProvider.credentialFromResult(result);
          const accessToken = credential.accessToken;
          const idToken = credential.idToken;
      
          // IdP data available using getAdditionalUserInfo(result)
          // ...
        })
        .catch((error) => {
          // Handle Errors here.
          const errorCode = error.code;
          const errorMessage = error.message;
          // The email of the user's account used.
          const email = error.customData.email;
          // The credential that was used.
          const credential = OAuthProvider.credentialFromError(error);
      
          // ...
        });

      Web

      firebase
        .auth()
        .signInWithPopup(provider)
        .then((result) => {
          /** @type {firebase.auth.OAuthCredential} */
          var credential = result.credential;
      
          // The signed-in user info.
          var user = result.user;
      
          // You can also get the Apple OAuth Access and ID Tokens.
          var accessToken = credential.accessToken;
          var idToken = credential.idToken;
      
          // IdP data available using getAdditionalUserInfo(result)
        // ...
        })
        .catch((error) => {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          // The email of the user's account used.
          var email = error.email;
          // The firebase.auth.AuthCredential type that was used.
          var credential = error.credential;
      
          // ...
        });
    • כדי להיכנס לחשבון על ידי הפניה לדף הכניסה, צריך להפעיל את הפונקציה signInWithRedirect():

    מומלץ לפעול לפי השיטות המומלצות כשמשתמשים ב-signInWithRedirect, ב-linkWithRedirect או ב-reauthenticateWithRedirect.

    Web

    import { getAuth, signInWithRedirect } from "firebase/auth";
    
    const auth = getAuth();
    signInWithRedirect(auth, provider);

    Web

    firebase.auth().signInWithRedirect(provider);

    אחרי שהמשתמש משלים את הכניסה ומוחזר לדף, אפשר לקבל את תוצאת הכניסה באמצעות קריאה ל-getRedirectResult():

    Web

    import { getAuth, getRedirectResult, OAuthProvider } from "firebase/auth";
    
    // Result from Redirect auth flow.
    const auth = getAuth();
    getRedirectResult(auth)
      .then((result) => {
        const credential = OAuthProvider.credentialFromResult(result);
        if (credential) {
          // You can also get the Apple OAuth Access and ID Tokens.
          const accessToken = credential.accessToken;
          const idToken = credential.idToken;
        }
        // The signed-in user info.
        const user = result.user;
      })
      .catch((error) => {
        // Handle Errors here.
        const errorCode = error.code;
        const errorMessage = error.message;
        // The email of the user's account used.
        const email = error.customData.email;
        // The credential that was used.
        const credential = OAuthProvider.credentialFromError(error);
    
        // ...
      });

    Web

    // Result from Redirect auth flow.
    firebase
      .auth()
      .getRedirectResult()
      .then((result) => {
        if (result.credential) {
          /** @type {firebase.auth.OAuthCredential} */
          var credential = result.credential;
    
          // You can get the Apple OAuth Access and ID Tokens.
          var accessToken = credential.accessToken;
          var idToken = credential.idToken;
    
          // IdP data available in result.additionalUserInfo.profile.
          // ...
        }
        // The signed-in user info.
        var user = result.user;
      })
      .catch((error) => {
        // Handle Errors here.
        var errorCode = error.code;
        var errorMessage = error.message;
        // The email of the user's account used.
        var email = error.email;
        // The firebase.auth.AuthCredential type that was used.
        var credential = error.credential;
    
        // ...
      });

    כאן אפשר גם לזהות ולטפל בשגיאות. רשימה של קודי השגיאה מופיעה בחומר העזר של ה-API.

    בניגוד לספקים אחרים שנתמכים על ידי אימות Firebase, Apple לא מספקת כתובת URL של תמונה.

    בנוסף, אם המשתמש בוחר לא לשתף את כתובת האימייל שלו עם האפליקציה, Apple מקצה לו כתובת אימייל ייחודית (בפורמט xyz@privaterelay.appleid.com) ומשתפת אותה עם האפליקציה. אם הגדרתם את שירות העברת האימייל הפרטי, Apple מעבירה אימיילים שנשלחים לכתובת האנונימית לכתובת האימייל האמיתית של המשתמש.

    Apple משתפת עם אפליקציות את פרטי המשתמשים, כמו השם המוצג, רק בפעם הראשונה שהמשתמש נכנס לחשבון. בדרך כלל, מערכת Firebase שומרת את שם התצוגה בפעם הראשונה שמשתמש נכנס באמצעות Apple. אפשר לקבל את שם התצוגה באמצעות firebase.auth().currentUser.displayName. עם זאת, אם השתמשתם בעבר ב-Apple כדי לאפשר למשתמש להיכנס לאפליקציה בלי להשתמש ב-Firebase, Apple לא תספק ל-Firebase את שם המשתמש המוצג.

אימות מחדש וקישור חשבונות

אפשר להשתמש באותו דפוס עם reauthenticateWithPopup() ו-reauthenticateWithRedirect(), כדי לאחזר פרטי כניסה עדכניים לפעולות רגישות שדורשות כניסה לאחרונה:

Web

import { getAuth, reauthenticateWithPopup, OAuthProvider } from "firebase/auth";

// Result from Redirect auth flow.
const auth = getAuth();
const provider = new OAuthProvider('apple.com');

reauthenticateWithPopup(auth.currentUser, provider)
  .then((result) => {
    // User is re-authenticated with fresh tokens minted and can perform
    // sensitive operations like account deletion, or updating their email
    // address or password.

    // The signed-in user info.
    const user = result.user;

    // You can also get the Apple OAuth Access and ID Tokens.
    const credential = OAuthProvider.credentialFromResult(result);
    const accessToken = credential.accessToken;
    const idToken = credential.idToken;

    // ...
  })
  .catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
    // The email of the user's account used.
    const email = error.customData.email;
    // The credential that was used.
    const credential = OAuthProvider.credentialFromError(error);

    // ...
  });

Web

const provider = new firebase.auth.OAuthProvider('apple.com');

firebase
  .auth()
  .currentUser
  .reauthenticateWithPopup(provider)
  .then((result) => {
    // User is re-authenticated with fresh tokens minted and can perform
    // sensitive operations like account deletion, or updating their email
    // address or password.
    /** @type {firebase.auth.OAuthCredential} */
    var credential = result.credential;

    // The signed-in user info.
    var user = result.user;
     // You can also get the Apple OAuth Access and ID Tokens.
    var accessToken = credential.accessToken;
    var idToken = credential.idToken;

    // IdP data available in result.additionalUserInfo.profile.
      // ...
  })
  .catch((error) => {
    // Handle Errors here.
    var errorCode = error.code;
    var errorMessage = error.message;
    // The email of the user's account used.
    var email = error.email;
    // The firebase.auth.AuthCredential type that was used.
    var credential = error.credential;

    // ...
  });

אפשר גם להשתמש ב-linkWithPopup() וב-linkWithRedirect() כדי לקשר ספקי זהויות שונים לחשבונות קיימים.

חשוב לזכור ש-Apple דורשת לקבל מהמשתמשים הסכמה מפורשת לפני שמקשרים את חשבונות Apple שלהם לנתונים אחרים.

לדוגמה, כדי לקשר חשבון Facebook לחשבון Firebase הנוכחי, משתמשים באסימון הגישה שקיבלת אחרי שהמשתמש נכנס ל-Facebook:

Web

import { getAuth, linkWithPopup, FacebookAuthProvider } from "firebase/auth";

const auth = getAuth();
const provider = new FacebookAuthProvider();
provider.addScope('user_birthday');

// Assuming the current user is an Apple user linking a Facebook provider.
linkWithPopup(auth.currentUser, provider)
    .then((result) => {
      // Facebook credential is linked to the current Apple user.
      // ...

      // The user can now sign in to the same account
      // with either Apple or Facebook.
    })
    .catch((error) => {
      // Handle error.
    });

Web

const provider = new firebase.auth.FacebookAuthProvider();
provider.addScope('user_birthday');

// Assuming the current user is an Apple user linking a Facebook provider.
firebase.auth().currentUser.linkWithPopup(provider)
    .then((result) => {
      // Facebook credential is linked to the current Apple user.
      // Facebook additional data available in result.additionalUserInfo.profile,

      // Additional Facebook OAuth access token can also be retrieved.
      // result.credential.accessToken

      // The user can now sign in to the same account
      // with either Apple or Facebook.
    })
    .catch((error) => {
      // Handle error.
    });

אימות באמצעות Firebase בתוסף ל-Chrome

אם אתם מפתחים אפליקציה לתוסף ל-Chrome, כדאי לעיין במדריך בנושא מסמכים מחוץ למסך.

חשוב לזכור שעדיין צריך לאמת את הדומיין המותאם אישית מול Apple, בדומה לדומיין ברירת המחדל firebaseapp.com:

http://auth.custom.example.com/.well-known/apple-developer-domain-association.txt

ביטול טוקן

Apple דורשת שאפליקציות שתומכות ביצירת חשבון חייבות לאפשר למשתמשים להתחיל את מחיקת החשבון שלהם בתוך האפליקציה, כפי שמתואר בהנחיות לבדיקה של App Store.

כדי לעמוד בדרישות האלה, צריך לבצע את השלבים הבאים:

  1. חשוב לוודא שמילאתם את הקטע Services ID ואת הקטע OAuth code flow configuration בהגדרת הספק של 'כניסה באמצעות Apple', כפי שמתואר בקטע הגדרת 'כניסה באמצעות Apple'.

  2. מאחר שמערכת Firebase לא שומרת אסימוני משתמשים כשמשתמשים נוצרים באמצעות 'כניסה באמצעות חשבון Apple', צריך לבקש מהמשתמש להיכנס שוב לפני ביטול האסימון ומחיקה של החשבון.

    לאחר מכן, מקבלים את אסימון הגישה של Apple OAuth מה-OAuthCredential, ומשתמשים בו כדי לבצע קריאה ל-revokeAccessToken(auth, token) כדי לבטל את אסימון הגישה של Apple OAuth.

    const provider = new OAuthProvider('apple.com');
    provider.addScope('email');
    provider.addScope('name');
    
    const auth = getAuth();
    signInWithPopup(auth, provider).then(result => {
      // Get the Apple OAuth access token.
      const credential = OAuthProvider.credentialFromResult(result);
      const accessToken = credential.accessToken;
    
      // Revoke the Apple OAuth access token.
      revokeAccessToken(auth, accessToken)
        .then(() => {
          // Token revoked.
    
          // Delete the user account.
          // ...
        })
        .catch(error => {
          // An error happened.
          // ...
        });
    });
    
  3. לבסוף, מוחקים את חשבון המשתמש (ואת כל הנתונים המשויכים).

מתקדם: אימות באמצעות Firebase ב-Node.js

כדי לבצע אימות באמצעות Firebase באפליקציה של Node.js:

  1. נכנסים עם המשתמש באמצעות חשבון Apple שלו ומקבלים את האסימון של Apple ID שלו. יש כמה דרכים לעשות זאת. לדוגמה, אם לאפליקציית Node.js יש ממשק קצה בדפדפן:

    1. בקצה העורפי, יוצרים מחרוזת אקראית (nonce) ומחשבים את הגיבוב שלה לפי SHA256. המזהה החד-פעמי הוא ערך לשימוש חד-פעמי שמשמש לאימות נסיעה הלוך ושוב אחת בין הקצה העורפי שלכם לבין שרתי האימות של Apple.

      Web

      const crypto = require("crypto");
      const string_decoder = require("string_decoder");
      
      // Generate a new random string for each sign-in
      const generateNonce = (length) => {
        const decoder = new string_decoder.StringDecoder("ascii");
        const buf = Buffer.alloc(length);
        let nonce = "";
        while (nonce.length < length) {
          crypto.randomFillSync(buf);
          nonce = decoder.write(buf);
        }
        return nonce.slice(0, length);
      };
      
      const unhashedNonce = generateNonce(10);
      
      // SHA256-hashed nonce in hex
      const hashedNonceHex = crypto.createHash('sha256')
        .update(unhashedNonce).digest().toString('hex');

      Web

      const crypto = require("crypto");
      const string_decoder = require("string_decoder");
      
      // Generate a new random string for each sign-in
      const generateNonce = function(length) {
        const decoder = new string_decoder.StringDecoder("ascii");
        const buf = Buffer.alloc(length);
        var nonce = "";
        while (nonce.length < length) {
          crypto.randomFillSync(buf);
          nonce = decoder.write(buf);
        }
        return nonce.slice(0, length);
      };
      
      const unhashedNonce = generateNonce(10);
      
      // SHA256-hashed nonce in hex
      const hashedNonceHex = crypto.createHash('sha256')
        .update(unhashedNonce).digest().toString('hex');
    2. בדף הכניסה, מציינים את המחרוזת החד-פעמית (nonce) שעברה גיבוב בהגדרות של הכניסה באמצעות Apple:

      <script src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
      <div id="appleid-signin" data-color="black" data-border="true" data-type="sign in"></div>
      <script>
          AppleID.auth.init({
              clientId: YOUR_APPLE_CLIENT_ID,
              scope: 'name email',
              redirectURI: URL_TO_YOUR_REDIRECT_HANDLER,  // See the next step.
              state: '[STATE]',  // Optional value that Apple will send back to you
                                 // so you can return users to the same context after
                                 // they sign in.
              nonce: HASHED_NONCE  // The hashed nonce you generated in the previous step.
          });
      </script>
      
    3. קבלת אסימון Apple ID מהתגובה לאימות שנשלחה ב-POST בצד השרת:

      app.post('/redirect', (req, res) => {
        const savedState = req.cookies.__session;
        const code = req.body.code;
        const state = req.body.state;
        const appleIdToken = req.body.id_token;
        if (savedState !== state || !code) {
          res.status(403).send('403: Permission denied');
        } else {
          // Sign in with Firebase using appleIdToken. (See next step).
        }
      });
      

    מומלץ גם לעיין במאמר הגדרת דף האינטרנט לכניסה באמצעות חשבון Apple.

  2. אחרי שמקבלים את האסימון של Apple ID של המשתמש, משתמשים בו כדי ליצור אובייקט Credential ואז נכנסים עם פרטי הכניסה של המשתמש:

    Web

    import { getAuth, signInWithCredential, OAuthProvider } from "firebase/auth";
    
    const auth = getAuth();
    
    // Build Firebase credential with the Apple ID token.
    const provider = new OAuthProvider('apple.com');
    const authCredential = provider.credential({
      idToken: appleIdToken,
      rawNonce: unhashedNonce,
    });
    
    // Sign in with credential form the Apple user.
    signInWithCredential(auth, authCredential)
      .then((result) => {
        // User signed in.
      })
      .catch((error) => {
        // An error occurred. If error.code == 'auth/missing-or-invalid-nonce',
        // make sure you're sending the SHA256-hashed nonce as a hex string
        // with your request to Apple.
        console.log(error);
      });

    Web

    // Build Firebase credential with the Apple ID token.
    const provider = new firebase.auth.OAuthProvider('apple.com');
    const authCredential = provider.credential({
      idToken: appleIdToken,
      rawNonce: unhashedNonce,
    });
    
    // Sign in with credential form the Apple user.
    firebase.auth().signInWithCredential(authCredential)
      .then((result) => {
        // User signed in.
      })
      .catch((error) => {
        // An error occurred. If error.code == 'auth/missing-or-invalid-nonce',
        // make sure you're sending the SHA256-hashed nonce as a hex string
        // with your request to Apple.
        console.log(error);
      });

השלבים הבאים

אחרי שמשתמש נכנס לחשבון בפעם הראשונה, נוצר חשבון משתמש חדש שמקושר לפרטי הכניסה – כלומר שם המשתמש והסיסמה, מספר הטלפון או פרטי ספק האימות – שבאמצעותם המשתמש נכנס לחשבון. החשבון החדש הזה מאוחסן כחלק מפרויקט Firebase, וניתן להשתמש בו כדי לזהות משתמש בכל האפליקציות בפרויקט, ללא קשר לאופן שבו המשתמש נכנס לחשבון.

  • באפליקציות, הדרך המומלצת לדעת את סטטוס האימות של המשתמש היא להגדיר משתמש מעקב על האובייקט Auth. לאחר מכן תוכלו לקבל את פרטי הפרופיל הבסיסיים של המשתמש מהאובייקט User. ניהול משתמשים

  • בכללי האבטחה של Firebase Realtime Database ו-Cloud Storage, אפשר לקבל את מזהה המשתמש הייחודי של המשתמש שנכנס לחשבון מהמשתנה auth, ולהשתמש בו כדי לקבוע לאילו נתונים למשתמש תהיה גישה.

כדי לאפשר למשתמשים להיכנס לאפליקציה באמצעות כמה ספקי אימות, אפשר לקשר את פרטי הכניסה של ספק האימות לחשבון משתמש קיים.

כדי להוציא משתמש מהחשבון, קוראים לפונקציה signOut:

Web

import { getAuth, signOut } from "firebase/auth";

const auth = getAuth();
signOut(auth).then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});

Web

firebase.auth().signOut().then(() => {
  // Sign-out successful.
}).catch((error) => {
  // An error happened.
});